chef-12.3.0/0000755000004100000410000000000012520074675012542 5ustar www-datawww-datachef-12.3.0/Rakefile0000644000004100000410000000420112520074675014204 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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 File.dirname(__FILE__) + '/lib/chef/version' require 'rubygems' require 'rubygems/package_task' require 'rdoc/task' require './tasks/rspec.rb' GEM_NAME = "chef" Dir[File.expand_path("../*gemspec", __FILE__)].reverse.each do |gemspec_path| gemspec = eval(IO.read(gemspec_path)) Gem::PackageTask.new(gemspec).define end task :install => :package do sh %{gem install pkg/#{GEM_NAME}-#{Chef::VERSION}.gem --no-rdoc --no-ri} end task :uninstall do sh %{gem uninstall #{GEM_NAME} -x -v #{Chef::VERSION} } end desc "Build it, tag it and ship it" task :ship => :gem do sh("git tag #{Chef::VERSION}") sh("git push opscode --tags") Dir[File.expand_path("../pkg/*.gem", __FILE__)].reverse.each do |built_gem| sh("gem push #{built_gem}") end end task :pedant do require File.expand_path('spec/support/pedant/run_pedant') end task :build_eventlog do Dir.chdir 'ext/win32-eventlog/' do system 'rake build' end end task :register_eventlog do Dir.chdir 'ext/win32-eventlog/' do system 'rake register' end end begin require 'yard' DOC_FILES = [ "README.rdoc", "LICENSE", "spec/tiny_server.rb", "lib/**/*.rb" ] namespace :yard do desc "Create YARD documentation" YARD::Rake::YardocTask.new(:html) do |t| t.files = DOC_FILES t.options = ['--format', 'html'] end end rescue LoadError puts "yard is not available. (sudo) gem install yard to generate yard documentation." end chef-12.3.0/bin/0000755000004100000410000000000012520074675013312 5ustar www-datawww-datachef-12.3.0/bin/chef-client0000755000004100000410000000160712520074675015425 0ustar www-datawww-data#!/usr/bin/env ruby # # ./chef-client - Run the chef client # # Author:: AJ Christensen () # 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 'rubygems' $:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) require 'chef' require 'chef/application/client' Chef::Application::Client.new.run chef-12.3.0/bin/chef-solo0000755000004100000410000000160712520074675015123 0ustar www-datawww-data#!/usr/bin/env ruby # # ./chef-solo - Run the chef client, in stand-alone mode # # Author:: AJ Christensen () # 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 'rubygems' $:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) require 'chef/application/solo' Chef::Application::Solo.new.run chef-12.3.0/bin/chef-shell0000755000004100000410000000213212520074675015250 0ustar www-datawww-data#!/usr/bin/env ruby # # ./chef-shell - Run the Chef REPL (Shell) # # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 # 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. begin require "rubygems" rescue LoadError end require "irb" require "irb/completion" require 'irb/ext/save-history' $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))) require "chef/shell" # On Windows only, enable irb --noreadline because of input problems -- # See CHEF-3284. IRB.conf[:USE_READLINE] = false if Chef::Platform::windows? Shell.start chef-12.3.0/bin/knife0000755000004100000410000000160012520074675014331 0ustar www-datawww-data#!/usr/bin/env ruby # # ./knife - Chef CLI interface # # Author:: Adam Jacob () # 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 'rubygems' $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))) require 'chef/application/knife' Chef::Application::Knife.new.run chef-12.3.0/bin/chef-apply0000755000004100000410000000160212520074675015267 0ustar www-datawww-data#!/usr/bin/env ruby # # ./chef-apply - Run a single chef recipe # # Author:: Bryan W. Berry () # Copyright:: Copyright (c) 2012 Bryan W. Berry # 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/application/apply' Chef::Application::Apply.new.run chef-12.3.0/spec/0000755000004100000410000000000012520074675013474 5ustar www-datawww-datachef-12.3.0/spec/data/0000755000004100000410000000000012520074675014405 5ustar www-datawww-datachef-12.3.0/spec/data/gems/0000755000004100000410000000000012520074675015340 5ustar www-datawww-datachef-12.3.0/spec/data/gems/chef-integration-test-0.1.0.gem0000644000004100000410000001700012520074675022665 0ustar www-datawww-datadata.tar.gz0000644000000000000000000001007300000000000013320 0ustar00wheelwheel00000000000000cJZmoG+#Zvd`?(# ȇL {FSU3=$-- Er^zz_]]]q}%k_?"~'??UpuoWTO7or'WF|춿mSLMםv;'Vmٳv;Ӽqg,M&W?ߩKr~?SonջL^|wd|.MikB'?Ӱ+]UjmtZl5n핮 7u!q&Sl\St9}uvїJ{UbP3Hx+jJ|ɻۑF;P)o6;gV58emwJwqx1rv[Nz7ǥRWꊅ,մ/V(>v{IF_^xYl]SeJ;?TkFocy^75 wmW"D׍c 64o# b;Ğsָ r -ok;Smr }$B];ֵ^|T w GZ 8ЁWvCbJ[|r{Z L"f)] 08⌏ oajlx4MDExZc3vik,qC@e)G霩}QOά38"8S6QAP9@W=%:dӔC%>1[,dR{$i"0ӈ11$[V [ D^! b!yF$ʧ,`צ^>,>&i) *025@ӌ̾C5V罕`M\`"@J66[ѓD%u)~[=>v?~OS6SYD5s9֌|Խg %<ƍ;wM瑕k>02.kFny-y4|0ViΦ{pz#[-VPca7pꦋ⤢J3I-ˋ+ZUHx]'0!PuM%FQ Pp5lkcZXc V[Ktn3v.([RtJgɂl=49} nGl(R`+[A 4JqM(iSOu2\_%x 8}}qg̦o#j^S-&[~0LXnN$7 Th\+nS;6xCmH\Oo6sM Y Ry- ,=l,!5g5r{,]&0 V'eSPd =g}H#5V+ߒcyIs(d{v)륦B+|2!-DMGF>ZUz;+ڡxSPň.*!]My#dI, ȟP"l#_qLhyTob( d˙5@eƋn}H=pyyapB6R=ǽڏ`R6tl?kcěeSBQ41$5I+apŶ~R *] .y"YMa8v[S0H„T}@ꄗ{#B5dMgZMk3-.{KK ^(ob Ct&Ş.}H#D&XaV@#I6+Ue1-u(qٞYud^@(0#),K\i8zO4BJhx+iv울 } >$aٍ=ܣ5iEQ` La~+ܶЄ(L]tGB:E[?(BV&,֍aQ/TR -߃" w5*gG,[)Y\r >K>磃iE:'V0 y^c0z|z?ະhd\rDܛEyt@XB aXiޅ<<4#gogq>ק7Brs ˨kf4s "Ty=<@ܬYA r+^H>-1SpXۙ;:d,{@bxb'!7Ʉfq0L=! iIKoȏ4`O/n~߱5yw_^\ϯmzb|Z]\s~} .b>l2Hp:;tln=Lz7Wl|}6~};WO/*Wwr}A^7\ܪn]IeP{,Az$,$l%j̛,B;ـPpރ.og\MnS 9h2=ԧ]FY%L\6syQO7z0x DJ?'x#{ '-s SCRO', :long => '--scro SCRO', :description => 'a configurable setting' attr_reader :ran def run @ran = true self # return self so tests can poke at me end end end chef-12.3.0/spec/data/knife_subcommand/test_name_mapping.rb0000644000004100000410000000010412520074675023723 0ustar www-datawww-datamodule KnifeSpecs class TestNameMapping < Chef::Knife end end chef-12.3.0/spec/data/knife_subcommand/test_explicit_category.rb0000644000004100000410000000035512520074675025016 0ustar www-datawww-datamodule KnifeSpecs class TestExplicitCategory < Chef::Knife # i.e., the cookbook site commands should be in the cookbook site # category instead of cookbook (which is what would be assumed) category "cookbook site" end endchef-12.3.0/spec/data/metadata/0000755000004100000410000000000012520074675016165 5ustar www-datawww-datachef-12.3.0/spec/data/metadata/quick_start/0000755000004100000410000000000012520074675020516 5ustar www-datawww-datachef-12.3.0/spec/data/metadata/quick_start/metadata.rb0000644000004100000410000000074112520074675022625 0ustar www-datawww-datamaintainer "Opscode, Inc." maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Example cookbook for quick_start wiki document" version "0.7" %w{ redhat fedora centos ubuntu debian macosx freebsd openbsd solaris }.each do |os| supports os end attribute "quick_start/deep_thought", :display_name => "Quick Start Deep Thought", :description => "A deep thought", :default => "If a tree falls in the forest..." chef-12.3.0/spec/data/cb_version_cookbooks/0000755000004100000410000000000012520074675020607 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/0000755000004100000410000000000012520074675021731 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/definitions/0000755000004100000410000000000012520074675024244 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/definitions/runit_service.rb0000644000004100000410000000010012520074675027441 0ustar www-datawww-data# IRL the runit_service is a definition to set up runit serviceschef-12.3.0/spec/data/cb_version_cookbooks/tatft/files/0000755000004100000410000000000012520074675023033 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/files/default/0000755000004100000410000000000012520074675024457 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/files/default/giant_blob.tgz0000644000004100000410000000003312520074675027301 0ustar www-datawww-data# not really a giant blob #chef-12.3.0/spec/data/cb_version_cookbooks/tatft/resources/0000755000004100000410000000000012520074675023743 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/resources/lwr.rb0000644000004100000410000000001112520074675025064 0ustar www-datawww-data# a LWR #chef-12.3.0/spec/data/cb_version_cookbooks/tatft/templates/0000755000004100000410000000000012520074675023727 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/templates/default/0000755000004100000410000000000012520074675025353 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/templates/default/configuration.erb0000644000004100000410000000000012520074675030702 0ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/README.rdoc0000644000004100000410000000017612520074675023543 0ustar www-datawww-data= THIS RECIPE * is for testing CookbookLoader/CookbookVersion * has at least one of every kind of file that cookbooks can havechef-12.3.0/spec/data/cb_version_cookbooks/tatft/providers/0000755000004100000410000000000012520074675023746 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/providers/lwp.rb0000644000004100000410000000000712520074675025072 0ustar www-datawww-data# a LWPchef-12.3.0/spec/data/cb_version_cookbooks/tatft/attributes/0000755000004100000410000000000012520074675024117 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/attributes/default.rb0000644000004100000410000000004012520074675026062 0ustar www-datawww-data#one_of_each default attributes chef-12.3.0/spec/data/cb_version_cookbooks/tatft/libraries/0000755000004100000410000000000012520074675023705 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/libraries/ownage.rb0000644000004100000410000000001012520074675025501 0ustar www-datawww-data# 0wnagechef-12.3.0/spec/data/cb_version_cookbooks/tatft/recipes/0000755000004100000410000000000012520074675023363 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/tatft/recipes/default.rb0000644000004100000410000000002412520074675025330 0ustar www-datawww-data# the default recipechef-12.3.0/spec/data/cb_version_cookbooks/cookbook2/0000755000004100000410000000000012520074675022477 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/cookbook2/files/0000755000004100000410000000000012520074675023601 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/cookbook2/files/test.txt0000644000004100000410000000000012520074675025307 0ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/cookbook2/templates/0000755000004100000410000000000012520074675024475 5ustar www-datawww-datachef-12.3.0/spec/data/cb_version_cookbooks/cookbook2/templates/test.erb0000644000004100000410000000000012520074675026134 0ustar www-datawww-datachef-12.3.0/spec/data/kitchen/0000755000004100000410000000000012520074675016032 5ustar www-datawww-datachef-12.3.0/spec/data/kitchen/openldap/0000755000004100000410000000000012520074675017634 5ustar www-datawww-datachef-12.3.0/spec/data/kitchen/openldap/definitions/0000755000004100000410000000000012520074675022147 5ustar www-datawww-datachef-12.3.0/spec/data/kitchen/openldap/definitions/drewbarrymore.rb0000644000004100000410000000005312520074675025356 0ustar www-datawww-data# # Was in people magazine this month... #chef-12.3.0/spec/data/kitchen/openldap/definitions/client.rb0000644000004100000410000000002312520074675023745 0ustar www-datawww-data# # A sad client # chef-12.3.0/spec/data/kitchen/openldap/attributes/0000755000004100000410000000000012520074675022022 5ustar www-datawww-datachef-12.3.0/spec/data/kitchen/openldap/attributes/default.rb0000644000004100000410000000004612520074675023773 0ustar www-datawww-data# # Nothing to see here, move along # chef-12.3.0/spec/data/kitchen/openldap/attributes/robinson.rb0000644000004100000410000000002712520074675024177 0ustar www-datawww-data# # Smokey lives here #chef-12.3.0/spec/data/kitchen/openldap/recipes/0000755000004100000410000000000012520074675021266 5ustar www-datawww-datachef-12.3.0/spec/data/kitchen/openldap/recipes/gigantor.rb0000644000004100000410000000005012520074675023420 0ustar www-datawww-datacat "blanket" do pretty_kitty true endchef-12.3.0/spec/data/kitchen/openldap/recipes/woot.rb0000644000004100000410000000003212520074675022576 0ustar www-datawww-data# # Such a funny word.. # chef-12.3.0/spec/data/kitchen/openldap/recipes/ignoreme.rb0000644000004100000410000000004212520074675023414 0ustar www-datawww-data# # this file will never be seen #chef-12.3.0/spec/data/kitchen/chefignore0000644000004100000410000000021512520074675020064 0ustar www-datawww-data# # The ignore file allows you to skip files in cookbooks with the same name that appear # later in the search path. # recipes/ignoreme\.rb chef-12.3.0/spec/data/run_context/0000755000004100000410000000000012520074675016755 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/nodes/0000755000004100000410000000000012520074675020065 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/nodes/run_context.rb0000644000004100000410000000014112520074675022756 0ustar www-datawww-data## # Nodes should have a unique name ## name "compile" run_list "test", "test::one", "test::two" chef-12.3.0/spec/data/run_context/cookbooks/0000755000004100000410000000000012520074675020746 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/0000755000004100000410000000000012520074675023401 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/definitions/0000755000004100000410000000000012520074675025714 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb0000644000004100000410000000006412520074675031627 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep1-definition') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/resources/0000755000004100000410000000000012520074675025413 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb0000644000004100000410000000006212520074675027565 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep1-resource') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/metadata.rb0000644000004100000410000000005512520074675025506 0ustar www-datawww-dataname "circular-dep1" depends "circular-dep2" chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/providers/0000755000004100000410000000000012520074675025416 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb0000644000004100000410000000006212520074675027573 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep1-provider') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/attributes/0000755000004100000410000000000012520074675025567 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb0000644000004100000410000000012612520074675027537 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "circular-dep1::default" chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/libraries/0000755000004100000410000000000012520074675025355 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb0000644000004100000410000000005212520074675026445 0ustar www-datawww-dataLibraryLoadOrder.record("circular-dep1") chef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/recipes/0000755000004100000410000000000012520074675025033 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep1/recipes/default.rb0000644000004100000410000000000012520074675026772 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/0000755000004100000410000000000012520074675023754 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/definitions/0000755000004100000410000000000012520074675026267 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb0000644000004100000410000000006612520074675032557 0ustar www-datawww-dataLibraryLoadOrder.record('no-default-attr-definition') chef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/resources/0000755000004100000410000000000012520074675025766 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb0000644000004100000410000000006412520074675030142 0ustar www-datawww-dataLibraryLoadOrder.record('no-default-attr-resource') chef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/providers/0000755000004100000410000000000012520074675025771 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb0000644000004100000410000000006412520074675030150 0ustar www-datawww-dataLibraryLoadOrder.record('no-default-attr-provider') chef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/attributes/0000755000004100000410000000000012520074675026142 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb0000644000004100000410000000012612520074675027774 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "no-default-attr::server" chef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/recipes/0000755000004100000410000000000012520074675025406 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/no-default-attr/recipes/default.rb0000644000004100000410000000000012520074675027345 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/0000755000004100000410000000000012520074675023146 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/definitions/0000755000004100000410000000000012520074675025461 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb0000644000004100000410000000006212520074675031055 0ustar www-datawww-dataLibraryLoadOrder.record('dependency2-definition') chef-12.3.0/spec/data/run_context/cookbooks/dependency2/resources/0000755000004100000410000000000012520074675025160 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/resources/resource.rb0000644000004100000410000000006012520074675027330 0ustar www-datawww-dataLibraryLoadOrder.record('dependency2-resource') chef-12.3.0/spec/data/run_context/cookbooks/dependency2/providers/0000755000004100000410000000000012520074675025163 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/providers/provider.rb0000644000004100000410000000006012520074675027336 0ustar www-datawww-dataLibraryLoadOrder.record('dependency2-provider') chef-12.3.0/spec/data/run_context/cookbooks/dependency2/attributes/0000755000004100000410000000000012520074675025334 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/attributes/default.rb0000644000004100000410000000012312520074675027301 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "dependency2::default" chef-12.3.0/spec/data/run_context/cookbooks/dependency2/libraries/0000755000004100000410000000000012520074675025122 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/libraries/lib.rb0000644000004100000410000000005012520074675026210 0ustar www-datawww-dataLibraryLoadOrder.record("dependency2") chef-12.3.0/spec/data/run_context/cookbooks/dependency2/recipes/0000755000004100000410000000000012520074675024600 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency2/recipes/default.rb0000644000004100000410000000000012520074675026537 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/0000755000004100000410000000000012520074675023627 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/definitions/0000755000004100000410000000000012520074675026142 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb0000644000004100000410000000006512520074675032304 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-deps-definition') chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/resources/0000755000004100000410000000000012520074675025641 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb0000644000004100000410000000006312520074675030014 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-deps-resource') chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/metadata.rb0000644000004100000410000000010212520074675025725 0ustar www-datawww-dataname "test-with-deps" depends "dependency1" depends "dependency2" chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/providers/0000755000004100000410000000000012520074675025644 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb0000644000004100000410000000006312520074675030022 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-deps-provider') chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/attributes/0000755000004100000410000000000012520074675026015 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb0000644000004100000410000000012612520074675027765 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "test-with-deps::default" chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/libraries/0000755000004100000410000000000012520074675025603 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb0000644000004100000410000000005212520074675026673 0ustar www-datawww-dataLibraryLoadOrder.record("test-with-deps") chef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/recipes/0000755000004100000410000000000012520074675025261 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/recipes/default.rb0000644000004100000410000000000012520074675027220 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-deps/recipes/server.rb0000644000004100000410000000000012520074675027102 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/0000755000004100000410000000000012520074675023145 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/definitions/0000755000004100000410000000000012520074675025460 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb0000644000004100000410000000006212520074675031053 0ustar www-datawww-dataLibraryLoadOrder.record('dependency1-definition') chef-12.3.0/spec/data/run_context/cookbooks/dependency1/resources/0000755000004100000410000000000012520074675025157 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/resources/resource.rb0000644000004100000410000000006012520074675027327 0ustar www-datawww-dataLibraryLoadOrder.record('dependency1-resource') chef-12.3.0/spec/data/run_context/cookbooks/dependency1/providers/0000755000004100000410000000000012520074675025162 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/providers/provider.rb0000644000004100000410000000006012520074675027335 0ustar www-datawww-dataLibraryLoadOrder.record('dependency1-provider') chef-12.3.0/spec/data/run_context/cookbooks/dependency1/attributes/0000755000004100000410000000000012520074675025333 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/attributes/default.rb0000644000004100000410000000012212520074675027277 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "dependency1::default" chef-12.3.0/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb0000644000004100000410000000012312520074675027342 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "dependency1::zz_last" chef-12.3.0/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb0000644000004100000410000000012312520074675027444 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "dependency1::aa_first" chef-12.3.0/spec/data/run_context/cookbooks/dependency1/libraries/0000755000004100000410000000000012520074675025121 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/libraries/lib.rb0000644000004100000410000000005012520074675026207 0ustar www-datawww-dataLibraryLoadOrder.record("dependency1") chef-12.3.0/spec/data/run_context/cookbooks/dependency1/recipes/0000755000004100000410000000000012520074675024577 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/dependency1/recipes/default.rb0000644000004100000410000000000012520074675026536 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/0000755000004100000410000000000012520074675021725 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/definitions/0000755000004100000410000000000012520074675024240 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/definitions/new_cat.rb0000644000004100000410000000016212520074675026204 0ustar www-datawww-datadefine :new_cat, :is_pretty => true do cat "#{params[:name]}" do pretty_kitty params[:is_pretty] end end chef-12.3.0/spec/data/run_context/cookbooks/test/definitions/new_animals.rb0000644000004100000410000000023412520074675027061 0ustar www-datawww-datadefine :new_dog, :is_cute => true do dog "#{params[:name]}" do cute params[:is_cute] end end define :new_badger do badger "#{params[:name]}" end chef-12.3.0/spec/data/run_context/cookbooks/test/definitions/test_res.rb0000644000004100000410000000005312520074675026413 0ustar www-datawww-dataLibraryLoadOrder.record('test-definition') chef-12.3.0/spec/data/run_context/cookbooks/test/resources/0000755000004100000410000000000012520074675023737 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/resources/resource.rb0000644000004100000410000000005112520074675026107 0ustar www-datawww-dataLibraryLoadOrder.record('test-resource') chef-12.3.0/spec/data/run_context/cookbooks/test/providers/0000755000004100000410000000000012520074675023742 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/providers/provider.rb0000644000004100000410000000005112520074675026115 0ustar www-datawww-dataLibraryLoadOrder.record('test-provider') chef-12.3.0/spec/data/run_context/cookbooks/test/attributes/0000755000004100000410000000000012520074675024113 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/attributes/default.rb0000644000004100000410000000000012520074675026052 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/attributes/george.rb0000644000004100000410000000004012520074675025702 0ustar www-datawww-datadefault[:george] = "washington" chef-12.3.0/spec/data/run_context/cookbooks/test/recipes/0000755000004100000410000000000012520074675023357 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test/recipes/default.rb0000644000004100000410000000005412520074675025327 0ustar www-datawww-data cat "einstein" do pretty_kitty true end chef-12.3.0/spec/data/run_context/cookbooks/test/recipes/two.rb0000644000004100000410000000013212520074675024511 0ustar www-datawww-datacat "peanut" do pretty_kitty true end new_cat "fat peanut" do pretty_kitty false end chef-12.3.0/spec/data/run_context/cookbooks/test/recipes/one.rb0000644000004100000410000000013012520074675024457 0ustar www-datawww-datacat "loulou" do pretty_kitty true end new_cat "birthday" do pretty_kitty false end chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/0000755000004100000410000000000012520074675025431 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/0000755000004100000410000000000012520074675027744 5ustar www-datawww-data././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootchef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rbchef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-d0000644000004100000410000000007612520074675033727 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-circular-deps-definition') chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/resources/0000755000004100000410000000000012520074675027443 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb0000644000004100000410000000007412520074675031620 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-circular-deps-resource') chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb0000644000004100000410000000006712520074675027541 0ustar www-datawww-dataname "test-with-circular-deps" depends "circular-dep1" chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/providers/0000755000004100000410000000000012520074675027446 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb0000644000004100000410000000007412520074675031626 0ustar www-datawww-dataLibraryLoadOrder.record('test-with-circular-deps-provider') chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/0000755000004100000410000000000012520074675027617 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb0000644000004100000410000000013712520074675031571 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "test-with-circular-deps::default" chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/0000755000004100000410000000000012520074675027405 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb0000644000004100000410000000006412520074675030500 0ustar www-datawww-dataLibraryLoadOrder.record("test-with-circular-deps") chef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/recipes/0000755000004100000410000000000012520074675027063 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/test-with-circular-deps/recipes/default.rb0000644000004100000410000000000012520074675031022 0ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/0000755000004100000410000000000012520074675023402 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/definitions/0000755000004100000410000000000012520074675025715 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb0000644000004100000410000000006412520074675031631 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep2-definition') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/resources/0000755000004100000410000000000012520074675025414 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb0000644000004100000410000000006212520074675027566 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep2-resource') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/metadata.rb0000644000004100000410000000005512520074675025507 0ustar www-datawww-dataname "circular-dep2" depends "circular-dep1" chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/providers/0000755000004100000410000000000012520074675025417 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb0000644000004100000410000000006212520074675027574 0ustar www-datawww-dataLibraryLoadOrder.record('circular-dep2-provider') chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/attributes/0000755000004100000410000000000012520074675025570 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb0000644000004100000410000000012512520074675027537 0ustar www-datawww-dataset_unless[:attr_load_order] = [] set[:attr_load_order] << "circular-dep2::default" chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/libraries/0000755000004100000410000000000012520074675025356 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb0000644000004100000410000000005212520074675026446 0ustar www-datawww-dataLibraryLoadOrder.record("circular-dep2") chef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/recipes/0000755000004100000410000000000012520074675025034 5ustar www-datawww-datachef-12.3.0/spec/data/run_context/cookbooks/circular-dep2/recipes/default.rb0000644000004100000410000000000012520074675026773 0ustar www-datawww-datachef-12.3.0/spec/data/definitions/0000755000004100000410000000000012520074675016720 5ustar www-datawww-datachef-12.3.0/spec/data/definitions/test.rb0000644000004100000410000000015212520074675020222 0ustar www-datawww-datadefine :rico_suave, :rich => "smooth" do zen_master "test" do something "#{params[:rich]}" end endchef-12.3.0/spec/data/lwrp_override/0000755000004100000410000000000012520074675017270 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp_override/resources/0000755000004100000410000000000012520074675021302 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp_override/resources/foo.rb0000644000004100000410000000021112520074675022404 0ustar www-datawww-data# Starting with Chef 12 reloading an LWRP shouldn't reload the file anymore actions :never_execute attribute :ever, :kind_of => String chef-12.3.0/spec/data/lwrp_override/providers/0000755000004100000410000000000012520074675021305 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp_override/providers/buck_passer.rb0000644000004100000410000000026212520074675024133 0ustar www-datawww-data# Starting with Chef 12 reloading an LWRP shouldn't reload the file anymore action :buck_stops_here do log "This should be overwritten by ../lwrp_override/buck_passer.rb" end chef-12.3.0/spec/data/shef-config.rb0000644000004100000410000000170512520074675017125 0ustar www-datawww-dataOhai::Config[:disabled_plugins] << 'darwin::system_profiler' << 'darwin::kernel' << 'darwin::ssh_host_key' << 'network_listeners' Ohai::Config[:disabled_plugins] << "virtualization" << "darwin::virtualization" Ohai::Config[:disabled_plugins] << 'darwin::uptime' << 'darwin::filesystem' << 'dmi' << 'lanuages' << 'perl' << 'python' << 'java' Ohai::Config[:disabled_plugins] << "linux::block_device" << "linux::kernel" << "linux::ssh_host_key" << "linux::virtualization" Ohai::Config[:disabled_plugins] << "linux::cpu" << "linux::memory" << "ec2" << "rackspace" << "eucalyptus" << "ip_scopes" Ohai::Config[:disabled_plugins] << "solaris2::cpu" << "solaris2::dmi" << "solaris2::filesystem" << "solaris2::kernel" Ohai::Config[:disabled_plugins] << "solaris2::virtualization" << "solaris2::zpools" Ohai::Config[:disabled_plugins] << 'c' << 'php' << 'mono' << 'groovy' << 'lua' << 'erlang' Ohai::Config[:disabled_plugins] << "kernel" << "linux::filesystem" << "ruby" chef-12.3.0/spec/data/standalone_cookbook/0000755000004100000410000000000012520074675020423 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/Gemfile0000644000004100000410000000003612520074675021715 0ustar www-datawww-datasource "https://rubygems.org/"chef-12.3.0/spec/data/standalone_cookbook/vendor/0000755000004100000410000000000012520074675021720 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/0000755000004100000410000000000012520074675023171 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/0000755000004100000410000000000012520074675024152 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/0000755000004100000410000000000012520074675024607 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/0000755000004100000410000000000012520074675025542 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/0000755000004100000410000000000012520074675030370 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/0000755000004100000410000000000012520074675031136 5ustar www-datawww-data././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootchef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rbchef-12.3.0/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_j0000644000004100000410000000003312520074675032520 0ustar www-datawww-data# This is a dummy ruby filechef-12.3.0/spec/data/standalone_cookbook/chefignore0000644000004100000410000000030112520074675022451 0ustar www-datawww-data# # The ignore file allows you to skip files in cookbooks with the same name that appear # later in the search path. # recipes/ignoreme.rb # comments can be indented ignored vendor/bundle/* chef-12.3.0/spec/data/standalone_cookbook/recipes/0000755000004100000410000000000012520074675022055 5ustar www-datawww-datachef-12.3.0/spec/data/standalone_cookbook/recipes/default.rb0000644000004100000410000000003212520074675024021 0ustar www-datawww-data# # Nothing ot see here # chef-12.3.0/spec/data/ssl/0000755000004100000410000000000012520074675015206 5ustar www-datawww-datachef-12.3.0/spec/data/ssl/5e707473.00000644000004100000410000000205612520074675016277 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC6DCCAlGgAwIBAgIJANlevg7kzqvpMA0GCSqGSIb3DQEBBQUAMFcxITAfBgNV BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEeMBwGA1UECxMVU25ha2VvaWwg Q2VydGlmaWNhdGVzMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMDkxMjE5MTkxODUy WhcNMTAwMTE4MTkxODUyWjBXMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQxHjAcBgNVBAsTFVNuYWtlb2lsIENlcnRpZmljYXRlczESMBAGA1UEAxMJ bG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhm9En1DL3aC4H j5/SA6FXm6B/0AoVzoPfWX2rpkRcz/XX24JEhQhLXStjhDr4p/IrARnZ8shy0MA4 wNpNPEn5c0RvqKypHzX+AeQkBx8J1/8vnMAoM9b/4pd0FqgRW1UbhvqQDzkWmVyK Tz5yCiTntxDzudAtHlTo8V6E7UEDkwIDAQABo4G7MIG4MB0GA1UdDgQWBBTmAcyA CqQblJ1L4sOIzmkdIAtY6jCBiAYDVR0jBIGAMH6AFOYBzIAKpBuUnUviw4jOaR0g C1jqoVukWTBXMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxHjAc BgNVBAsTFVNuYWtlb2lsIENlcnRpZmljYXRlczESMBAGA1UEAxMJbG9jYWxob3N0 ggkA2V6+DuTOq+kwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBe4f9R s0g5GCFekabzl9AHvIn4ITxenvuyaNX9f2BJbdgoD03wlGycBxjbC57RjFVfetu7 mtUYuJSx7iojBSC+LzotGptrG9d2BxrWOKBfF2K+dyoIG8kZL5aLfS0be6Cc5O3c L/IPadJhBu/EfyGI2vL1l8GspXdOxaFzHprpgA== -----END CERTIFICATE----- chef-12.3.0/spec/data/ssl/private_key_with_whitespace.pem0000644000004100000410000000322412520074675023503 0ustar www-datawww-data -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh 8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD 2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/ /RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4 DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8 OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12 2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ== -----END RSA PRIVATE KEY----- chef-12.3.0/spec/data/ssl/key.pem0000644000004100000410000000156712520074675016512 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDhm9En1DL3aC4Hj5/SA6FXm6B/0AoVzoPfWX2rpkRcz/XX24JE hQhLXStjhDr4p/IrARnZ8shy0MA4wNpNPEn5c0RvqKypHzX+AeQkBx8J1/8vnMAo M9b/4pd0FqgRW1UbhvqQDzkWmVyKTz5yCiTntxDzudAtHlTo8V6E7UEDkwIDAQAB AoGAGicC1tgdVFqqQ0wd3a14DXzH3SkTkjWPSdvI2pX6hLvCptQWRLUbIglZ1z5j y6FETEHjakVfgRe7wJhyddOQS3eeVt/aK0xBHz/JiJuIF+NzbJT9t01nPV21abYU lWIhWV8Ja39a5LKV6hee0TTYdAub7BVQ95kwrqMqRcDoXHECQQDxpAgq925Cmlz1 0Q1WZq2A/o8oqPvPS1FulPK2OgyOyQSK+DdcK2xUKGWMn0m9fDLLzj/pe/H3dN1I b8Z/iiWrAkEA7wPlesZX3GzfqQLd6GYGBa4IdrV5dHdeoCCVRnkFr06KjcqpAhg1 7i0T9frSC5EfRCfbGNgo4eutT9+D7HJhuQJAZeDBrNPbQetxDBbSp73sovkwhHUS jah0scnMtvWse7rW1nymYo7QQn8xqWMzJNerVvAjVB50ut8juLmfmAA3twJAQy9/ NBHI5Mcd365Unlz/WF1hN60vZNOhH7XJADRIqsyTGeRbuaEAl+DH+Z71qBa1CT2C 0usAIvFSmF8mADLu0QJAHSSh6zLNInvkhDjYAmEu3oeFQgQ4Rp7oiMaBZ6VVuOMo 4GU9CA18iI75NaO7FOfquJPkIJ0li0xadVofUpaJcg== -----END RSA PRIVATE KEY----- chef-12.3.0/spec/data/ssl/private_key.pem0000644000004100000410000000321712520074675020236 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh 8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD 2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/ /RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4 DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8 OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12 2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ== -----END RSA PRIVATE KEY----- chef-12.3.0/spec/data/ssl/chef-rspec.cert0000644000004100000410000000315712520074675020112 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIEkjCCA3qgAwIBAgIJAKBJr4wSRUVvMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEN MAsGA1UEChMEQ2hlZjETMBEGA1UECxMKZGV2ZWxvcGVyczESMBAGA1UEAxMJa2Fs bGlzdGVjMR4wHAYJKoZIhvcNAQkBFg9kYW5Ab3BzY29kZS5jb20wHhcNMTAwNDEw MTkxMTMxWhcNMjAwNDA3MTkxMTMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT Cldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzAR BgNVBAsTCmRldmVsb3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3 DQEJARYPZGFuQG9wc2NvZGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAw5l9EtBHsJrb5AIxARP695an3v+509gOXRKnjWIRnU+knbdTnEdjlGGG SxuFR7Fnp2OM8ed7iPIKSrcM0vQ+g7vYKCv5Z8UR3sbLY8UHm9AgZ/bLAHEHS2if 1WHPD5DOe1B7HwW0IfEiW4/WakkVn4uoWw5rCZ87f4YCrETomXIo1n/rMFHf+yoY guuEfGQxRcQdlEZM9YMlMByQvXlVR5IVhpiMHBCyV6KzxjZVCgTlvS8nPMiiHpoO pgB6BGEQ/nn4Kapk40baPqpT4EP/DnBnbhhR3kBQ6MQRlh7bl5vjH5xFSFwGUUA9 IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABo4H0MIHxMB0GA1UdDgQWBBS88Zxt vG+FTu1U+VFA47ffzwStbjCBwQYDVR0jBIG5MIG2gBS88ZxtvG+FTu1U+VFA47ff zwStbqGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x EDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzARBgNVBAsTCmRldmVs b3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3DQEJARYPZGFuQG9w c2NvZGUuY29tggkAoEmvjBJFRW8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUF AAOCAQEAwwMrbuJAhP5uawJi5OYEaJKSbJGyahCcOAl4+ONgsdDoCy/9AZKzuFNc C8vM/Ee6jyugrKMdckvZ883kJ4770HU6nbomCUVKMHMzJBE1Guvsn8wZP3nKyeSZ eXXbH1b/NfstNyo6XLucaBRQvyvQYDUnk6osrBh+Gekvqsahr0wkVa8VUY2UySyY 60lYt4O92XJ1jWtYoFjRxeeUgo5E0TfIWj74kXhdMqwMf4Iv9VatfYR87ps5VMdf Hp+nrCRaquDAs87LdO9e7M8l+W1ryPfP2inuGjIozsN5lLmwBdT+O6NkpmuxGPEG ArIbYatR7+4MsDn+CjfkYblnmGLuug== -----END CERTIFICATE----- chef-12.3.0/spec/data/ssl/chef-rspec.key0000644000004100000410000000321312520074675017736 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAw5l9EtBHsJrb5AIxARP695an3v+509gOXRKnjWIRnU+knbdT nEdjlGGGSxuFR7Fnp2OM8ed7iPIKSrcM0vQ+g7vYKCv5Z8UR3sbLY8UHm9AgZ/bL AHEHS2if1WHPD5DOe1B7HwW0IfEiW4/WakkVn4uoWw5rCZ87f4YCrETomXIo1n/r MFHf+yoYguuEfGQxRcQdlEZM9YMlMByQvXlVR5IVhpiMHBCyV6KzxjZVCgTlvS8n PMiiHpoOpgB6BGEQ/nn4Kapk40baPqpT4EP/DnBnbhhR3kBQ6MQRlh7bl5vjH5xF SFwGUUA9IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABAoIBAQC+hddKaA4se+sL 4QaSoj+mwtypXjZHnv/+sJj8IjY+IMGbzmJmqzLX6VbB+gCMoMTySwmS54NxFTHp LPwUz0vFTUdzecHpzg9mDAU5HUYYA1ZNbhq2R2JvlW16j1b9NnOpse77fLbFCPgK b8TOqnmheot2hkjEipGN2Z7o5gYaz1/3PtolkP1ypCTG6Bh7V3ohBLBIEdjA552o HNGe3t6PpvoNtBqaeb/j/SAOvg+8DGF1WQtE+5Y1koSlhABYWkHzHC1fHAzRMSHH ZMfKOQNusRgBRNJabdVqkuTbvyRCQEb2YGQxPPYV2C+AxAlh3APeYTg90vUqAq/3 ivNdilcBAoGBAOLELc0mcTftDbIMWVnrzAGAJOCMz3FkwGcV8nqNeA3R77e3pWL2 5+bKadWQGjjpR3ZEYt/RxHsoGCW3NtM44icxqVCTPW/unp2xqadjuvcsKrxk+1wD OdvVrwcd/N+KzgXO+Hm7xbV/loFms3ueGfCRbOueQyP4dj9MyOBGlO2hAoGBANzQ u8IrZBG0DL8YFdmjw4YWUENIOtABPU1qHo/sugTQjI9K3/E3LA7aaGnl2P//1tao SR/aP/To90H6D989/JomhkEKKA+DyL1sRL1NMdtWwrKdEq32W8fUN0JEA+Q1FMsd Hk6Ix+KrZVg9cTb9HoGikDxeHW3pPKDWaEkWIQLLAoGAD13N4L3/JBQLPop5r487 9soRNao1EHEMXK/vC4D0prMYNHHcYjVrB4el3lPygvLD5e7CaHpVfyb7Y+rjazLK mG9UEuK3YhNgaj00yuQGMmOqzbNmGRka3ZvATZIppZhJV7lruwwPXLo1n7Uu6myP Q28HW3wQ/qoCkU2JuzDtPKECgYBUrYcTEuixEUbCEU5vw6k7RltJMe27zn3frg5C Sxmatw7v9Fqkee/fUkowMgBhS47rimVgXaWhGaWYG3jytyajRpq9XlO2f2b/nQFP RscTwdWwASQkqhDQNMVsGAEWBnUO3v+8Rh/BANFAYW+FEtQcCmcdf0nx2DtzwkUD ogTOuQKBgCbEg+/ND/p8xKwY9LtjLKnrQSL5tSH/7prhLJvVVdW7FMRfKSp1t2xc kfJFqO1Lcf2j7hiclval3xDoWUretNQ5379T0Ob30WuIomSfeqcxJjCUtyN3fUqr z/QG9dk/23OOYJhRgAmttBDqpk5uB5mOQgSftdELNyw0EOyNIBfZ -----END RSA PRIVATE KEY----- chef-12.3.0/spec/data/templates/0000755000004100000410000000000012520074675016403 5ustar www-datawww-datachef-12.3.0/spec/data/templates/seattle.txt0000644000004100000410000000011012520074675020575 0ustar www-datawww-dataSeattle is a great town. Next time you visit, you should buy me a beer.chef-12.3.0/spec/data/nodes/0000755000004100000410000000000012520074675015515 5ustar www-datawww-datachef-12.3.0/spec/data/nodes/default.rb0000644000004100000410000000040312520074675017463 0ustar www-datawww-data## # Nodes should have a unique name ## name "test.example.com-default" ## # Nodes can set arbitrary arguments ## default[:sunshine] = "in" default[:something] = "else" ## # Nodes should have recipes ## run_list "operations-master", "operations-monitoring" chef-12.3.0/spec/data/nodes/test.example.com.rb0000644000004100000410000000042112520074675021225 0ustar www-datawww-data## # Nodes should have a unique name ## name "test.example.com" ## # Nodes can set arbitrary arguments ## normal[:sunshine] = "in" normal[:something] = "else" ## # Nodes should have recipes ## run_list "operations-master", "operations-monitoring" chef_environment "dev" chef-12.3.0/spec/data/nodes/test.rb0000644000004100000410000000040112520074675017014 0ustar www-datawww-data## # Nodes should have a unique name ## name "test.example.com-short" ## # Nodes can set arbitrary arguments ## default[:sunshine] = "in" default[:something] = "else" ## # Nodes should have recipes ## run_list "operations-master", "operations-monitoring" chef-12.3.0/spec/data/bootstrap/0000755000004100000410000000000012520074675016422 5ustar www-datawww-datachef-12.3.0/spec/data/bootstrap/secret.erb0000644000004100000410000000033212520074675020377 0ustar www-datawww-databash -c ' <% if encrypted_data_bag_secret -%> awk NF > /etc/chef/encrypted_data_bag_secret <<'EOP' <%= encrypted_data_bag_secret %> EOP chmod 0600 /etc/chef/encrypted_data_bag_secret <% end -%> <%= config_content %>' chef-12.3.0/spec/data/bootstrap/no_proxy.erb0000644000004100000410000000004112520074675020764 0ustar www-datawww-databash -c ' <%= config_content %>' chef-12.3.0/spec/data/bootstrap/encrypted_data_bag_secret0000644000004100000410000000002612520074675023507 0ustar www-datawww-datasupersekret_from_file chef-12.3.0/spec/data/bootstrap/test-hints.erb0000644000004100000410000000050312520074675021214 0ustar www-datawww-data<%# Generate Ohai Hints -%> <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%> mkdir -p /etc/chef/ohai/hints <% end -%> <% @chef_config[:knife][:hints].each do |name, hash| -%> ( cat <<'EOP' <%= Chef::JSONCompat.to_json(hash) %> EOP ) > /etc/chef/ohai/hints/<%= name %>.json <% end -%> chef-12.3.0/spec/data/bootstrap/test.erb0000644000004100000410000000005412520074675020072 0ustar www-datawww-data<%= Chef::JSONCompat.to_json(first_boot) %> chef-12.3.0/spec/data/remote_directory_data/0000755000004100000410000000000012520074675020755 5ustar www-datawww-datachef-12.3.0/spec/data/remote_directory_data/remote_subdirectory/0000755000004100000410000000000012520074675025046 5ustar www-datawww-datachef-12.3.0/spec/data/remote_directory_data/remote_subdirectory/remote_subdir_file.txt0000644000004100000410000000007512520074675031453 0ustar www-datawww-dataI'm a file in a subdirectory inside a remote_directory sourcechef-12.3.0/spec/data/remote_directory_data/remote_dir_file.txt0000644000004100000410000000007112520074675024644 0ustar www-datawww-dataI'm a file inside a the root remote_directory source dir.chef-12.3.0/spec/data/null_config.rb0000644000004100000410000000005612520074675017232 0ustar www-datawww-data$__KNIFE_INTEGRATION_FAILSAFE_CHECK << " ole" chef-12.3.0/spec/data/environment-config.rb0000644000004100000410000000006712520074675020544 0ustar www-datawww-data# # Sample Chef Config File # environment "production"chef-12.3.0/spec/data/remote_file/0000755000004100000410000000000012520074675016677 5ustar www-datawww-datachef-12.3.0/spec/data/remote_file/nyan_cat.png.gz0000644000004100000410000003514012520074675021623 0ustar www-datawww-dataK"Qnyan_cat.png2uP<[p` . \%@kpw.rq.A7v뽪gkkwfz>_(%i4d"d(((4Y O0S h5i1i8ߡ~%S˨)BAyBAq ;ԙ &> SUw OKCD Io6Y[f !(FayYg  L hmg*<+/2YGK0Ozh|z+ձqñdžg=McעMRZ:*y|GZ0y1L !p8(HlO}L'h<"J(49e~BB,6Rhk@F18e >ӆL>bp=[m(+ETR {c3G3>kRGւZy[Ց0~QVӟh T4d`ШAtzE3XBUt /c~:ڿ+g郫 4*Qi;"+<=S׈ IÐG c&[lDZրI- {oLvCRaiEr3{M>gڞQ!?,eaDAb\EMH|Gt0w™["Ez :([#zC+hnXD/=l;[bY Oۆ;2ƍ\}+C1WCA@zB(R ࡷ;7n?0'Ǖh'S2b̒U b3Y44 V8O2:%<5` ={ s` {|LR07lhY[B+"| >T{7+ iT\2C 2t~zqzLRhi22%cUl^N'wI QK'UFo~3}?CEiGґqB]-#: I]x)eAj)x$vYjн kvDctaKErD1YI>}g@$ƶHGߨU^ǼA3daTKo|9oj>:$jݮB< 2?34(64`\@CjI!$/6 +$=ZI!G~_U[LT2W5^8 NB" l, K<'vz6?m'VÉ#xD|d T@ )wWm'LE хԖF0tQ9JU9,sk9Oa璫pMp,z#f?0K%}Th  ;~So -όz!cwR%>]&e|:ObLXO6aI&R;y檑G(ЦcKsu X`U2aKq0ƤQEB@GMQFAcJT[c'T'ێ et_bM,Suk = f/Pwљ6>'KLPy$:Owe*i7X;-27G|Hs=~ }E4x~ofI'xO^}iE}'0r#H,xyp)jy锹 Tf۰yY';KU&iK&C0[E7RZ8onR,kDH4WWA @9xϚ"'XIU?T+-ߚ6P Њ;Ei-bɁ:]oQV->䬱>)fɴ>);j0yųQ_5(7RhK9/bϕ3wO0J,t M.MYn Mb+Yi-S>Fyb1P6uc! 9 H*70Tvv8e/Ս,L1`wa=bnʉ}OvO1sٸJ5^(NպbNK;Jю"g`OcãS_D~3SonlD cOzGcХ'DYlP1d<yAY1DfTfxRuH\#rn:u%t;*l;׬ךq~Ѽ"-=:48 03шȫ$g!T"TQw7*AfYm݌+&G.RWti(U,\  f@RHErD m%_9I5AbvoDNf4#0(8ua›YH$Z= ǔA``ҪSrSi`+ #{P;@;2}on)BǮt?_|vj} ;]D{1ݚ\K#ǽOL3v&e8(?;+pEA;N 6{>Tg t?{?-11T22ly Dpx Ns4 '~S3]! bVά?RhSSš49 Vs`\ˏkwyϸ n |8#{ɔ+%ynݶfS]|SsLFU KI[9ELa7PLϼA(Ɣ#t25m)놼Zyoq0iRu%ƎR%5ƃ lAo ``Q9>E㲾Vc (;K2'.1QhƀmR/V5xݪ~?}=EI ƞt@hEnsݱVwcQcQ_M9U$Ƚ}S~A>a"X` gB{)wȁ? ќ: `BiRvl5g\x=V6龝:pF׼k:{%Ńv+1ZiM%$D 7 (˪+"=&vHw8v\udc'6P3?9C e/-{z~W+[ĒScO'8aܿ| B͡Z&/pAr bO1Ssl}r9U,jPۦAΉƵ=8\Uuy?Zh+W?ŻsH 2+)C?bmT 2nJ.w"w.Zl9If9. 0/3yU\(4wo|p6y65x{nn4CLΠC q {+۫LaS5U|$wBx >n d:F;u!jE6PEx5H;ƶd< ;L` A]{M~Ft|7v}'Tp @$3V5mZ++1r 7͙5 RلPͣ7IݗE(\ZMhXq'!HCd1 V^!el_@ؑ=`)YgKXy+\eS֩eas̱BMXۥPT@32C;2YA#Z:4XJg.Sp.Xv!8,9nKLP+0 bFy g_S>&x3>x-||t+׼mu\fr./IUG# }fҶvgRi%ӅVnfLsikG\&`!jTTJz8WDKeoc!{Fblqdn|48uqlK|?8>80){],5JFj|\5JǙ7rdwn$=SHoD֠ ː_D|M Ys9rhS唩.h9L+OWfڜ BT Oc *T[*je3##ux!@kߜyA;w j;84tHU+۬QHg^3. r38 mʋI Ș{=C<*0\)Hx-ɼ;VEO 9a-U(sA/K #}R SeAӬ$_DZ t^W8,! "+`oRbpv8N z7tZɨՆߒžLI;b((F,~P~0C Z@ cI? ZR0ϖw<{D W4r/,'6?^ g[pp(&8QS#w$|Sc7y,t[t <:[fCB48: uR5Fh!@W ۃ :ɊG&ߝi:iMX%:xFin""av[]\OBGᄥQΔS̯~~s~cCl44aa#VW54C6Z5:x^4cYiE2M_a_{?c!lˣP[ {: kcd 93THIݡHzU@4 X,Ai̮ZXABG)?Wh,"<a4OY>rwf'0%X2C?덝AٚpHl?hoƃ7?qP <φbJWד{S)L6+yyb,H!q ?|rЊFbY8~:S.ePKmƘȃ~bTǽPVT-))/6(sxS~UU.弝_Ҷ:8ʕtÈH08P6 '=0f5-{!vUzS&h|zV`yQ͊q'u#*ǔmCG]7k 95k}9=io5|<+' Uύt @I`qo@[~͏rElZk*w8im-#e^rqM>_;R O. ;'2k$lNqR jMe9zդ׆D豿-,-nj͚`́0D؝pb ڳX>b/p?ojvk-_t5x?#3` 3tЂPM 3}&]4Jt†]+z { OO .<jńl=i^~Y%;FD]*Umƿj t,ab=jOq¾ݗc &|~1|!Ft .Ty*Azw (uRf&eXC*Ty?Ta涘(jO^X9qWƶ(~-tB[wOIᩮ"d)&Zm,:a zn* ٕ4q2_'&u yaTthW>.-sG+lR&*`O28%an(_̀A'? Dظ? #kqp% vY _s}aNG_ўU# QFbJB(70/ 7>BUCPu]r}] y_,")4ؒܮZH=H%f F?)Hn-+@FYI$ 2^T{!n4S}_ENx br8hznOu9oV&饝q?hD?AAyÊ ʿl {_P. pSLġR!vPg]؁,,hvCp[U w&՝|N%٫\k.q(onei݀q=l6OQ4ļ8=)rܷ̿ZQwa.=Fh"T/mH ~QotvAQp]>Dhߤpz]vn J8bnai'A-*~jmb,'Nѐe%D6Ӄ\Z6V9_Í!gϳo  tg|Vќ&B/8xA˲aB崃!sOG9V3xusҒ ŅmK Sha3 A(܎ zS;aԤ2=iѺwb^ :QNtB}B[maXR@-jĆJ띓 zMiז{J5t ^ W+$s+7+7@+Jyn ({=b}@bNJyM`6)Wx]LCIC*܀B ao|`6 MmmPܹ,ןOK_{}6LZb|sV^ B@|aAޝZkI<'u\\9W"}2?=#MҶ v{[ôsWòog3A$PE 5+fyj\;`Ԃzi]$MSho+@DF&74Z'HEŕ%Rps3YBIH !>rWz ?c=[08TOvonED#Zl0}{15 M WEh([]Nw|q┺#nr5k.${pC 7v:n>,O3u1YۿMb X# KtNSs_ۗST/G?>TОKk*zskws~sNB\/%Y>W5OX #huuC$MnO]pI!i^3F>xEyq߸M.SOH$/7^p)gxk'G3NrzT%] ˕XPq0Vw+}挪4FCĂp?ڤٍ 麷At0a,/3>ٷy0y[P`xt O"H#pP8@Ϝ(&Y|aU\9vaClxFbE$Gܹdhƾŭ]fR|4 RT|oB aRs{m9j{ť;u;gX~s[up(YZ\na譴nĦS ohã~'Fy?﷯[Hs6? Syj ɚ9wr*~ R 9`T0:>mЛ ]o~gJXA:r@39ӹ̔7ًDkGoB|n3xXpΘ_b0p[{T\lnZ9;M 81=x!t0's?QȆݳ\Y{`ș's }_n+Mwfez^!&Cxah0 هVPvμG3Y) i8Ce(.i[Sw s+JrFD_[ar d*lj/݅k'f ءt\N<9:jB #i*_-ʻY3<]f_py,8$n𻔤xz_?P#dksl`|T"lIb(a{^=6d d*_?}%) cw6h奙FII!}^nn@cd I,hX݀0o{ڡge _^sIkĻiX?w0bApFAEHF] +(}t.Wvؤjb gʗ`2aՌuVШԋNqE5;11zŵC?Zofō?M&?.K¼LK)9r0Qp:ve}μ,F8yl UR ߍ'z ΫT1[^NTm[I3yDߔf.OzTA!]k=O>7k%KxsAWS.$d7ʒO4o>+ޣi;<^,p΂+:nbd:}ݘ!jJF]<\eayq~TB::|ȝx'߆!O64;⯑Γ;Nع 㭷Uy:mc\f? ؇ٳ|LWCD&o3R3c9Y}9ma"Խ7 E,x%k[$J?K UEwGs]D&_(,bhH 5@YjKE[{MlZU9'Qe귰ϣC]/$DsKx gKs}+WVWL׉t2%GLxRg;DJ{V{379=Eʾ ]&~jxWv.jtUu[4nMd}"C-#gɥQ^aO{\-g6P4eó]u@im| uyy<7Üx BЮ!KtϋHR5+ֱPMzsh ;jKNxELL܌+彨t(~^MNZRgτ&(>OPH͢1,/oPh_9 8+5zu" w[4/t%[7=CP'zZ*HJ]bۂؤK%K>Sæã?}tHY ^G Mr*۴ cd#oM&A;ٿ-%uC9:<}wm)>^h@hmqN&Z<9'IY倹=_# Իp-@+|*ɤIn~yƚkGSosYu1nhHiEIPK"4'a \xKUJ_X}c-%:wj%m˝bwce{eƚpp- z- QvȢ VY$U|7!jQ[#wR߸[ OOJgɿx(Ws2_^0,ث,[am9<|xB!H%Ը*+=eXbkjfC4XV%LgRE2&[˷x@9PRr}0L)ZCȦL 4-ɀEBm~AygY~)kxcKbj|7i3{ټKjȼey_ '655,|{3vG=RĞ}͠{8rO=4TCa18LOO%ƪIZpmCFJr2;el=b1OüFߧ[5?9uѝ6%Hٹ楝0 ~fJGޘ=7}}M8٩Y7<1k&d-Z)Eo/u0M{ bg HKerv]JőfW3`t,tߘ[yʷu#{Qy"DhB3*>?1wIO\RpZH5%1/5cxQ&pʨ_PE]Jb&A\F?b;chef-12.3.0/spec/data/remote_file/nyan_cat.png0000644000004100000410000003554212520074675021212 0ustar www-datawww-dataPNG  IHDRjsRGBgAMA a cHRMz&u0`:pQ<:IDATx^]`:"Ez/I,XЧ 5*`(IriPDT|O_TĆ)"HIof6{{{$ޔooi6⸕TeEGGJ)ق/Icc ϡ(ji >L)%R:IH⦄ #=˳Bƪ2_GU 겻Xy_y/ϞŤ:^:uR. lAɤ@_bTd(1T5{Fh<VxewQ}5gO&L#%BH \WΉS;'ɏ#G·$~{4.>Tio Qw]Z~#{. T](6X z^нlBGI\(9qRݾx~_&mid կYnϞaILOHyeb) %P]o { o_]r,$ 55H E$ܥ,$jjzTV»![;5jУc#i5!%uSޗMTCFMrرqӄ m b̠H^cOҎ7EASaϦv&HJv'.8vE<ݽ&ygBzXaw>j`7ؒ[k@~݃zC ёppoC۟ΟEw~7:|?u͒p7QR_DOD8"5Ќ%0i 3W~./Tq1.DCo_ۖCa@>"BvϪ ~_xsO撄Pn3Ur/P45?wC (%}pns(ǖK F+.bY HK XHjeUl& w) IBMJNJ@]JBPSnp,Tۤ$ܥ,$ 5U6) w) IBMMJ@]JBPSnp,Tۤ$ܥ,$ 5U6) w) IBM$͛pn *įSXh5M,@%BP$ty>\ L5$LcWPk5~HH7.g-(6ĕ> 1&<pI ]o'O?y uZG~`4hapΤN;W2@J \%)%$ %`* ' kG+I"vԀ+c%seeZR,a"vp_„vWܳ?Ͼiӯ?cF6 aBwrνd&+62pK=~\kyHz8,4Ǐ{k~w;^=2bp9Gx}ݡ}|&Vaf3;^u쐟;0@^iq,ݥjK6AT1R(g0)*z _7YJ i HK XHjV*^Etj鏯HLX ("pLh#6 F+Q;$ty⤦z\ &Cn'Ud$ܥBX7g{.{QIb&E{Iȫ7gC *R,Q=Sa4=uQ>jѳW9GGſpbciT87NJcT=P=1:9̶DjSUz^qBJm6JFx|TԲ=Nq O!=?U(Nx OQDpR:^v%g G=Lto+41`7o@J+ \`QBfl|p׼)xp XnGI㿄`k1Jz4muf=lt'm{~{Ktdy+`m9~ ICH1!rvL4" {~zhz`S,XB8RaX-SUŘL}RNc;ÂSeo*ٞx[vU;Z=5ˢ+a1+32fc'[Up7[A~漓ac/ T p&HBB'dA:]??a({ v83ȡ;K۔-SC?r_x1;m+8W3u WKQw6*c;|.f޼5Pa-z3oeW&FI~8*T2VB &ofMw,שL-9!QrQ%:iN啅EKݿ:K]KRp,dυ?uUG,Jlvb!l,ȫh)}Gj[0Jt]A-oBKi&]FEҸXZ}-p)cl K[K;O׼|PLrH?; 6NS ^YkDi Uъظ |> 6OF o{O)kpw@;k^ޤOQ<5Tf+I9ʅ%Ǖ|UCQ^Nޣ$f+}TxjQYլJX@Y4f lZ5&eU`f3T)gjT61nˣ~ e>un>%}ƯTfӸtl ϫj5r?&zjNke*Q!Bw :M"Q^N^#WTh렣"WJ?=!4*Lo>Ly0ģn?I3^2'eʊS)§&A l2hY t/^Hݎ*pB ^-}E}ýSLYVI e_EsȏUVF~x?]U=Sl#f lOдwv@ `Mh߉QƵn:D9=|7eYl'XJa6KQQXa1bSO=% XzB*2DM j0Tj' ̧o+|'GUNp7-(N˞nECT%)mB 3!z:vpߘQ}[5GYCM〺7Grk+k̰a`deejY@4 +m:hI 9t_z;a˻f`cȴ;fu[LWv3z8B}=_-;-:?!̓rѮbxrŅO)Rl**6PaWՠ}5pOMME͛ʟ_zURUE%M0pTI6f4ca]*7l0Gp{Z3fKZrGa6F894vU}pQ{`]o^{+jNvS%oSVB,$wn wp3?!7_$}՛&&&nZ3OgY9q3} ]wUe; ÿkZ+VjQ_=S^koůW]uY0\h-ሄRh1HJZRҕzRB,( T*0d';ULp耭_~ڵp[N dӦM&L.hn\Kl`W6$D -竨;[9 ի m4DEq!h w3Cost$UOJ(Qѓ%0It-:juUy6…<&y3| &9#GCX~*,ì`,=,8U' LR'ڶ06klm4Pkh!=ԏZGz{ΝI4kJ?&̰#hW*R)'-BR 饐8=Y2@!𤋮z`ʘ>/!}qnC?=#`F{,IbRsA ļyĄ} HC\D})ٍfܬQ 0y4|c>3Pw|JǾL7{p3g=uԑ׏->n8|"Mus0%R{]wݍ7xcZ'}lHH &XƨܓVjdIgŸ$ ueaT=ְI ≸+‡ٽlwL0Zfl(UbEyW^(w !.O!rM#Mh5hnS/J3$̠tuL&lPU!^H*&u/i%7n0.gLp1rމ^ZUye=+}mTfz&q?m޼m~曳gxGEܟc0 E=s(筷Bxl1]Ldx0~LG׉,w:Nw5B+T&tIt œo^$;&c?'P ذe)4O1 VޖG~T=]mWFzF݁BX\MIIЇEQw^K/T4ZQ[pX7ԛP"1KU;9{&4jXxʖTl~YhFQ1Q^>|8 Vip7øW%%&=^4ȝ2'l{N`;L={qg\2". ٶm9qB碨ѣGy펾~ǰdT/1$jjH9kwaO{-1k`':Q ~w']ɣ-pݿ[M%_tEa&MQ^`%V 駟jEᢨQFIcbIy=\^ise_ֿMx6hWРoՠ v ԯ_c_IQőKo7ߝ3v1m&_9lw1_ 0a@ 7o\p,YKh.8]s=\h( p3fʢkv籨]ͱP,GǢWG" *KPZю1}чq=|4v1ţfÒ{KwxHŲUiii]?`͑/M5xQUw[--K5cf1Ne&f*;;hg'='NU} 07"=z/$x}[}M7%MHvUn_nnĩp|#n6,"ZVTZ΃|ٮ+ Z4n l_H's?~mK` #Gawe3cR0{,-5hwgrr~='HnOx47'|;̛'v!\I}.H\/hzæJ`̛ܹY WhaϊH;`Ľ )Ӛ7J(Q; BᦕyǘO- aU0|ĥ sa&Æcr!#Iu3|j>}|$Vɴ #uY:" ;>nb0FX,btVx1A鵔b$Q;TXg~>uzUF&ܙ}?'vcr0CРAFE^Cz~ Ęo X ̶K"0fl/b`͔[ⴙ'1sj3#99rsiNNYnny8 #bÌą^ɡ8Wlӹ";RʫC%N'-,<p3^^*CE! fgg-'`Fl0eح{Vh`gp|,ѭ!)½+b, ׬Y|)O 0v~.1"=,~{-tV:Zث-u:H0FmyPecJ=WՂ s w&uGa7`X&wIq>^r.,i^x6f˜y<@"w1roY. w-~_>Lv1FLA^h/\NҔeřlߖ) ۼ%LXYsMD(0]ڷ~$3b.̡ۣxl I$J=T;qB؍;zSVғ[zRC,K:nJԦO(gzuffa<}@_H䵘`">)^ͧr4ܦ _Ϻc ?y[W*wegUvg)oQMAo5GQӇ4EU9/%tiӫy>8,oN bZ x͜9S|'P#4=C;$6%۲"ﰚ)}X:ޚ%h4 ɼW^ >ů>|F[e.gϙQ VnφU3N'ݕ #d~1DsC$0D=Y=r`mP=p7D_hbmTܘF+L#w08y1$O{]NO:PUR_{;sӒN(q記zb!M=lPV%z/x[Vl2*7ٍ!jИeY':;o9M&.hq,ot)VFS0-ԎpHk1‘{ADM1TUWT|CE%֧$wW;7[N,zh-S,<߈( Gy>%|Vaw//Wи<x<( X'wߖ#s#h0O)TZ&RpOO*H7}6} ;;n?԰ZbcL711q=8a Mh0iD (5o#Wկ¸SNX$;VM0 `ZAYO[Y%C1֪֫ӓJ,4qM,-"ĵaX`>ᝦd,:iCII2< ٽpI:l\1c{=#tPlmpq\ _{S1O 4=~\;3cϨnX_ *}EGhA߀5G=]lvSkyX; Kl(hN'[ ilkwx4: PK5!vfGIuh;$a*@]zsI9NKҴ^lؒ4lÖ7應`FXP[9r;VwsUt\U;p(cG{9pꑲy1p,څ1uvРn&)cI;Qt``![vctw|V,_5^?죁-3`J@OkA".|0fD2VJ7:uIHɲeo\f)] \-E^A`jޯPg9KqQD'f:d&;xǟu3t>2)lPgddh]UOC#=I<`i"tyw@"V BVwfpoWn=  +FwOius8/pc WL=S֮֬]sϚ}s&V$eUӮj㟦x ;d 9*sPfc3;_{RRLl~b; ZHZ%٠[܁vl-:\ql%#ąMܳ+&pv*m QVj_^NJHvIjwyζrTÊՅ3YqD004Gq.ZQ F`2y??]dlKNk59<7.&z`0@Uq#4bye`Mړ': eO8L:$arLjغ0Z3j~%16K.6 ҘBz]_AvJVx]xN: C lsVMu FNF}S^y @{PgzU$:/B@ඊS*6k[ ;<OcrU8**rU!ӿy,w> PKT9V+[ 84V!:%ܥ,$ 5::Lԟ$ܥ,$ 5U<) w) IBMMJ@]JBPSnp,Tۤ$ܥ,$ 5U6) w) IBMMJ@]JBPSnp,Tۤ$ܥ,$ 55Bu[t~S@ $ J]F71:۝L #4 JTomw-n~`$S Hll?Co⻟? I]=@ ~օR{~Y{/[$!RmW~mk wULCVpph3[1s!R@$5UCU=,RGvgtxc\˧`#M9!rHRxVl '5"HKݖ|+>۟QLIi%q6S>|$W40H܈v=+nN2ɅjI*6JAkO qdw?$`7< h0^n/ON?!:gP +6#3Md*" f>pQsI8?,~ڻ;{po\@ n#prÛ&pp/}/c, -(P]½6J@3s^>ˑ~G@>;3I<{3s<my#1s-YWmLmKG®L5%`j4AD{!3 S-nuTd.p#џ/hn.aX9$pOJЌ%Wߙ `LI>V3ᙁ\ ?d3ߝM.))Lr\T;~oʈ2y"=a'x2p!2{ɥ%$T쪆 !i-Pi[Ml*XIbAw@gY$i'@-y77HfȞ 5B0l\_BKRR*1) w) IBMMJ@]JBPSnp,Tۤ$ܥ,$ 5U6) w) IBMMJ@]JBPSn"١(bQ2@*1"H;>E)Z+*=ݞagqP ”LjD 88tO:}ĩlyвs=k-:;C)---S>p0e@jbpc6'J^9y\5$U{b/ڹ?{<|!Tݹ z#xj%V@*NYB+(^ER$p5J@]JBPSk,հ$ܥ,$ 5J@]JBPSk,լO " ?:IENDB`chef-12.3.0/spec/data/search_queries_to_transform.txt0000644000004100000410000000543712520074675022756 0ustar www-datawww-dataafield:[* TO *] content:afield__=__* afield:[a TO *] content:[afield__=__a TO afield__=__\ufff0] afield:[* TO b] content:[afield__=__ TO afield__=__b] *:* *:* role:mon content:role__=__mon role:mon AND role:prod content:role__=__mon AND content:role__=__prod run_list:role\[rubberband\] AND run_list:role\[whale\] content:run_list__=__role\[rubberband\] AND content:run_list__=__role\[whale\] sharable_server:[* TO *] content:sharable_server__=__* run_list:role\[nfs_server\] AND sharable_server:[* TO *] content:run_list__=__role\[nfs_server\] AND content:sharable_server__=__* run_list:role\[nfs_server\] AND sharable_server:[* TO *] content:run_list__=__role\[nfs_server\] AND content:sharable_server__=__* (role:prod AND x_y:true) (content:role__=__prod AND content:x_y__=__true) hostname:[* TO *] AND role:prod content:hostname__=__* AND content:role__=__prod role:t_mem AND role:prod NOT hostname:ip-1-2-3-4 content:role__=__t_mem AND content:role__=__prod NOT content:hostname__=__ip-1-2-3-4 ohai_time:[1234.567 TO *] content:[ohai_time__=__1234.567 TO ohai_time__=__\ufff0] ohai_time:{1234.567 TO *} content:{ohai_time__=__1234.567 TO ohai_time__=__\ufff0} ohai_time:[* TO baz] content:[ohai_time__=__ TO ohai_time__=__baz] ohai_time:{* TO baz} content:{ohai_time__=__ TO ohai_time__=__baz} tags:apples*.for.eating.com content:tags__=__apples*.for.eating.com role:safe AND ohai_time:[1234.567 TO *] AND whiz_bang:x5 content:role__=__safe AND content:[ohai_time__=__1234.567 TO ohai_time__=__\ufff0] AND content:whiz_bang__=__x5 role:safe AND ohai_time:[* TO 1234.567] AND whiz_bang:x5 content:role__=__safe AND content:[ohai_time__=__ TO ohai_time__=__1234.567] AND content:whiz_bang__=__x5 animal:[ape TO zebra] content:[animal__=__ape TO animal__=__zebra] animal:{ape TO zebra} content:{animal__=__ape TO animal__=__zebra} ((value:[1 TO 3] OR nested_b1_a2_a3:B1_A2_A3-c) OR value:[5 TO *]) ((content:[value__=__1 TO value__=__3] OR content:nested_b1_a2_a3__=__B1_A2_A3-c) OR content:[value__=__5 TO value__=__\ufff0]) ((value:{1 TO 3} OR value:{1 TO 3}) OR run_list:recipe\[alpha\]) ((content:{value__=__1 TO value__=__3} OR content:{value__=__1 TO value__=__3}) OR content:run_list__=__recipe\[alpha\]) words:"one two three" content:"words__=__one two three" words:"one \"two\" three" content:"words__=__one \"two\" three" words:"\"one two\" three" content:"words__=__\"one two\" three" words:"one two \"three\"" content:"words__=__one two \"three\"" words:"one two \"three\"" OR words:"\"one two\" three" AND words:"one \"two\" three" content:"words__=__one two \"three\"" OR content:"words__=__\"one two\" three" AND content:"words__=__one \"two\" three" words:\"* content:words__=__\"* -version:0.9.12 -content:version__=__0.9.12 !version:0.9.12 NOT content:version__=__0.9.12 ec2:* content:ec2__=__* chef-12.3.0/spec/data/apt/0000755000004100000410000000000012520074675015171 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.1/0000755000004100000410000000000012520074675021671 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/0000755000004100000410000000000012520074675023113 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/files0000644000004100000410000000006112520074675024135 0ustar www-datawww-datachef-integration-test_1.1-1_amd64.deb ruby extra chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/copyright0000644000004100000410000000167612520074675025060 0ustar www-datawww-dataThis work was packaged by: Joshua Timberman > on Thu, 30 Sep 2010 09:53:45 -0600 Upstream Author(s): Opscode, Inc. Copyright: Copyright (C) 2010 Opscode, Inc License: 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. The Debian packaging is: Copyright (C) 2010 Opscode, Inc () and is licensed under the Apache 2.0 license. See "/usr/share/common-licenses/Apache-2.0" chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/control0000644000004100000410000000067312520074675024524 0ustar www-datawww-dataSource: chef-integration-test Section: ruby Priority: extra Maintainer: Joshua Timberman > Build-Depends: debhelper (>= 7.0.50~) Standards-Version: 3.8.4 Homepage: http://tickets.opscode.com Package: chef-integration-test Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Chef integration tests for APT in Cucumber This package is used for cucumber integration testing in Chef. chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/source/0000755000004100000410000000000012520074675024413 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/source/format0000644000004100000410000000001412520074675025621 0ustar www-datawww-data3.0 (quilt) chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/changelog0000644000004100000410000000051312520074675024764 0ustar www-datawww-datachef-integration-test (1.1-1) unstable; urgency=low * New upstream release (1.1) -- Joshua Timberman Thu, 30 Sep 2010 10:09:34 -0600 chef-integration-test (1.0-1) unstable; urgency=low * Initial release (Closes: #CHEF-1718) -- Joshua Timberman Thu, 30 Sep 2010 09:53:45 -0600 chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/compat0000644000004100000410000000000212520074675024311 0ustar www-datawww-data7 chef-12.3.0/spec/data/apt/chef-integration-test-1.1/debian/rules0000755000004100000410000000067212520074675024200 0ustar www-datawww-data#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ chef-12.3.0/spec/data/apt/chef-integration-test_1.1-1_amd64.deb0000644000004100000410000000327212520074675023564 0ustar www-datawww-data! debian-binary 1285863038 0 0 100644 4 ` 2.0 control.tar.gz 1285863038 0 0 100644 464 ` n0P 6%Rݘ,F$mr٭*͓c'ɥA%z6F)eZ hRVNe|IU鮅Ko\\x/KOXcuH6?wpo5#T ID |dE66ק( I"F  Al>3U6zPqd5(c2OhOپ=Z^3`ԀBtd{׉=SU^^CPz2NL16o,=  +)xLT;dT #D{)|*1GCh9(aD[}IovNvGAW@-V~_:l6UGN-+/#="}s1c}! b "D.j kF6‰g:o c! |݈́Nd@D r b9{*VF'*LNJH&&ah4\'Jb4JaXN%6³~ %QbuneQMR'γCQ`8DR$,؏K95Qg1kiwB䈰Wubk (=:6w*] "J6Vn^P{B/D"H$D"H$D>V( chef-12.3.0/spec/data/apt/chef-integration-test_1.1-1_amd64.changes0000644000004100000410000000144612520074675024443 0ustar www-datawww-dataFormat: 1.8 Date: Thu, 30 Sep 2010 10:09:34 -0600 Source: chef-integration-test Binary: chef-integration-test Architecture: amd64 Version: 1.1-1 Distribution: unstable Urgency: low Maintainer: Joshua Timberman > Changed-By: Joshua Timberman Description: chef-integration-test - Chef integration tests for APT in Cucumber Changes: chef-integration-test (1.1-1) unstable; urgency=low . * New upstream release (1.1) Checksums-Sha1: 43c5653a9a5b9419849173a4ec3a9855cf0327a3 1722 chef-integration-test_1.1-1_amd64.deb Checksums-Sha256: 84e2f087f7e11d1b73743007ecfc6b8b34e03f6917c0993b35c0758ee59702c1 1722 chef-integration-test_1.1-1_amd64.deb Files: 4b05bace483dbca54efc21f97ee47e1d 1722 ruby extra chef-integration-test_1.1-1_amd64.deb chef-12.3.0/spec/data/apt/var/0000755000004100000410000000000012520074675015761 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/0000755000004100000410000000000012520074675016605 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/0000755000004100000410000000000012520074675017371 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/pool/0000755000004100000410000000000012520074675020342 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/pool/main/0000755000004100000410000000000012520074675021266 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/0000755000004100000410000000000012520074675021510 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/0000755000004100000410000000000012520074675025713 5ustar www-datawww-data././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootchef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.debchef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.1-1_0000644000004100000410000000327212520074675032721 0ustar www-datawww-data! debian-binary 1285863038 0 0 100644 4 ` 2.0 control.tar.gz 1285863038 0 0 100644 464 ` n0P 6%Rݘ,F$mr٭*͓c'ɥA%z6F)eZ hRVNe|IU鮅Ko\\x/KOXcuH6?wpo5#T ID |dE66ק( I"F  Al>3U6zPqd5(c2OhOپ=Z^3`ԀBtd{׉=SU^^CPz2NL16o,=  +)xLT;dT #D{)|*1GCh9(aD[}IovNvGAW@-V~_:l6UGN-+/#="}s1c}! b "D.j kF6‰g:o c! |݈́Nd@D r b9{*VF'*LNJH&&ah4\'Jb4JaXN%6³~ %QbuneQMR'γCQ`8DR$,؏K95Qg1kiwB䈰Wubk (=:6w*] "J6Vn^P{B/D"H$D"H$D>V( ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootchef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.0-1_amd64.debchef-12.3.0/spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.0-1_0000644000004100000410000000322012520074675032711 0ustar www-datawww-data! debian-binary 1285863052 0 0 100644 4 ` 2.0 control.tar.gz 1285863052 0 0 100644 468 ` n0`~68$hVۤI0]d~zT'!mi\\UB,fge쯪LH`A:Jgm۸O٠8/c/e@ )VovC]{ Veveq+K1rL':Xմ%@#drShRv::$"=a .8rkI(;oˏ|CMYu7T]".gg \M?YϒЀH߽WVCpsC>1 Ճy N2nndu&k ߂`Z6އ0YzӳrfzsHHO |&x:=T?dn IWB!B!B!Bھ7(data.tar.gz 1285863052 0 0 100644 1020 ` _Ec * R?G^L Y܉k.9 N6mv;^O}Q8ŗ""B/'"ȩ (g7rV'e2;7Z -+φQ \6 S<X3"\D);OQE-,CֶFհֿKmko);c7B`cƵQ_WjUuw<ا:;{__T6ߴ_HMӬ.:q{6n'(;o}Br?u%3`_\]gc0vyb|Oܻ4\y⅟ μ=_oa_[9ҫ3*YzsG]zr#/|'ݏ]ْvFnNr q:^]b ;SU !A B#w0( x2/F&(>9=y̡]:?9 4C `?u?2a'*U]Wِ>bѨ]W%GUj PCQ ͇.AG= 5+%x GLT; ?\|,h!fX8#>|!H<e ݨCC'ÑGzK@|y8iI(V+Ӎx`?1DB:"J%@#@niID8 0㉸2?]"D!o8D đ Z`ֲ[Ds j33F۞nAsƔݶ 1{jCnLca %QZoGA;G]ƢpGq IX**!v3?>]kDlg #YAa]o胢(bM0-:-4[Y{xMR|I$D"H$D"H$WԨ(chef-12.3.0/spec/data/apt/var/www/apt/conf/0000755000004100000410000000000012520074675020316 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/conf/incoming0000644000004100000410000000011712520074675022043 0ustar www-datawww-dataName: default IncomingDir: /tmp/incoming TempDir: /tmp Allow: sid unstable>sid chef-12.3.0/spec/data/apt/var/www/apt/conf/distributions0000644000004100000410000000020212520074675023135 0ustar www-datawww-dataOrigin: localhost Label: apt repository Codename: sid Architectures: amd64 Components: main Description: Apt repository Pull: sid chef-12.3.0/spec/data/apt/var/www/apt/conf/pulls0000644000004100000410000000004512520074675021377 0ustar www-datawww-dataName: sid From: sid Components: main chef-12.3.0/spec/data/apt/var/www/apt/db/0000755000004100000410000000000012520074675017756 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/db/packages.db0000644000004100000410000004000012520074675022035 0ustar www-datawww-datab1   SP  SP sid|main|amd64b1   SP   TPackage: chef-integration-test Version: 1.1-1 Architecture: amd64 Maintainer: Joshua Timberman > Installed-Size: 32 Homepage: http://tickets.opscode.com Priority: extra Section: ruby Filename: pool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.deb Size: 1722 SHA256: 84e2f087f7e11d1b73743007ecfc6b8b34e03f6917c0993b35c0758ee59702c1 SHA1: 43c5653a9a5b9419849173a4ec3a9855cf0327a3 MD5sum: 4b05bace483dbca54efc21f97ee47e1d Description: Chef integration tests for APT in Cucumber This package is used for cucumber integration testing in Chef. chef-integration-testchef-12.3.0/spec/data/apt/var/www/apt/db/release.caches.db0000644000004100000410000005000012520074675023125 0ustar www-datawww-datab1   p7_P  p7_P sida p7_Pэh^pqrstuvwxyz{|}~ N3:1:91ca9531e3afa7a540cabdc6030c6f75d315fec7 :2:098c599ac5b0a98785336afb2bc9c47002570ffa07dd62321c6f70b9fdb74325 46cd71c965ce0813c94ef78c836cc7d3 104main/binary-amd64/Release:1:cde25071c5fcee59cee8dcd773ca419dcb40d946 :2:af601ce143f33405425746462973adc0fda3aceb381d1c739851b95ee0814ca3 92ed2cc14e37e9ab23466b27857d29ac 596main/binary-amd64/PackagesK K:1:ce04daff75d4b27371d691d645282b198045544a :2:15e98119705a08018d4583caabc91d36ba12e6f1c8af0f799a3ec8ca5bfaa80d c7726773341137b71cc971d44ddec4f5 394main/binary-amd64/Packages.gzchef-12.3.0/spec/data/apt/var/www/apt/db/references.db0000644000004100000410000004000012520074675022400 0ustar www-datawww-datab1   >`  >`  referencesb1  a>` sid|main|amd64Hpool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.debchef-12.3.0/spec/data/apt/var/www/apt/db/contents.cache.db0000644000004100000410000004000012520074675023156 0ustar www-datawww-datab1   -W3  -W3 compressedfilelistsb1   -W3 chef-12.3.0/spec/data/apt/var/www/apt/db/checksums.db0000644000004100000410000004000012520074675022244 0ustar www-datawww-datab1   ^]  ^] poolb1   ^] :1:43c5653a9a5b9419849173a4ec3a9855cf0327a3 :2:84e2f087f7e11d1b73743007ecfc6b8b34e03f6917c0993b35c0758ee59702c1 4b05bace483dbca54efc21f97ee47e1d 1722Hpool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.debchef-12.3.0/spec/data/apt/var/www/apt/db/version0000644000004100000410000000003712520074675021366 0ustar www-datawww-data4.2.0 3.3.0 bdb4.8.30 bdb4.8.0 chef-12.3.0/spec/data/apt/var/www/apt/dists/0000755000004100000410000000000012520074675020517 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/dists/sid/0000755000004100000410000000000012520074675021276 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/dists/sid/Release0000644000004100000410000000157112520074675022605 0ustar www-datawww-dataOrigin: localhost Label: apt repository Codename: sid Date: Thu, 30 Sep 2010 16:33:01 UTC Architectures: amd64 Components: main Description: Apt repository MD5Sum: 92ed2cc14e37e9ab23466b27857d29ac 596 main/binary-amd64/Packages c7726773341137b71cc971d44ddec4f5 394 main/binary-amd64/Packages.gz 46cd71c965ce0813c94ef78c836cc7d3 104 main/binary-amd64/Release SHA1: cde25071c5fcee59cee8dcd773ca419dcb40d946 596 main/binary-amd64/Packages ce04daff75d4b27371d691d645282b198045544a 394 main/binary-amd64/Packages.gz 91ca9531e3afa7a540cabdc6030c6f75d315fec7 104 main/binary-amd64/Release SHA256: af601ce143f33405425746462973adc0fda3aceb381d1c739851b95ee0814ca3 596 main/binary-amd64/Packages 15e98119705a08018d4583caabc91d36ba12e6f1c8af0f799a3ec8ca5bfaa80d 394 main/binary-amd64/Packages.gz 098c599ac5b0a98785336afb2bc9c47002570ffa07dd62321c6f70b9fdb74325 104 main/binary-amd64/Release chef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/0000755000004100000410000000000012520074675022222 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/0000755000004100000410000000000012520074675024417 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Release0000644000004100000410000000015012520074675025716 0ustar www-datawww-dataComponent: main Origin: localhost Label: apt repository Architecture: amd64 Description: Apt repository chef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages.gz0000644000004100000410000000061212520074675026476 0ustar www-datawww-datamRj }+I4jLB);v00þĠd>=s 3vFvnNx&9?D~ayQ^'Ki bdV^:q5&a23ސ~{,Oe\.oy.1+S,>1!8\(Ic Oo~m߶Eq]UTFb5F9BKFk`m+PjUY*;*Z e[Ff0!cR00Qi#룊 )ke#z FICeғGrCG?ţx>O =.1h.׈ [IK BGTchef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages0000644000004100000410000000112412520074675026056 0ustar www-datawww-dataPackage: chef-integration-test Version: 1.1-1 Architecture: amd64 Maintainer: Joshua Timberman > Installed-Size: 32 Homepage: http://tickets.opscode.com Priority: extra Section: ruby Filename: pool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.deb Size: 1722 SHA256: 84e2f087f7e11d1b73743007ecfc6b8b34e03f6917c0993b35c0758ee59702c1 SHA1: 43c5653a9a5b9419849173a4ec3a9855cf0327a3 MD5sum: 4b05bace483dbca54efc21f97ee47e1d Description: Chef integration tests for APT in Cucumber This package is used for cucumber integration testing in Chef. chef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-i386/0000755000004100000410000000000012520074675024175 5ustar www-datawww-datachef-12.3.0/spec/data/apt/var/www/apt/dists/sid/main/binary-i386/Packages0000644000004100000410000000000012520074675025624 0ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test_1.0-1_amd64.changes0000644000004100000410000000146012520074675024436 0ustar www-datawww-dataFormat: 1.8 Date: Thu, 30 Sep 2010 09:53:45 -0600 Source: chef-integration-test Binary: chef-integration-test Architecture: amd64 Version: 1.0-1 Distribution: unstable Urgency: low Maintainer: Joshua Timberman > Changed-By: Joshua Timberman Description: chef-integration-test - Chef integration tests for APT in Cucumber Changes: chef-integration-test (1.0-1) unstable; urgency=low . * Initial release (Closes: #CHEF-1718) Checksums-Sha1: b44685ff59626bc94c67e60665f06c4643fe9767 1680 chef-integration-test_1.0-1_amd64.deb Checksums-Sha256: da176f4405fa21fd7207d4785680c6996d395a1ca132f2d5565a61c5479b1116 1680 chef-integration-test_1.0-1_amd64.deb Files: 713722480408ecc8e7220aea52bdd76e 1680 ruby extra chef-integration-test_1.0-1_amd64.deb chef-12.3.0/spec/data/apt/chef-integration-test-1.0/0000755000004100000410000000000012520074675021670 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/0000755000004100000410000000000012520074675023112 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/files0000644000004100000410000000006112520074675024134 0ustar www-datawww-datachef-integration-test_1.0-1_amd64.deb ruby extra chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/copyright0000644000004100000410000000167612520074675025057 0ustar www-datawww-dataThis work was packaged by: Joshua Timberman > on Thu, 30 Sep 2010 09:53:45 -0600 Upstream Author(s): Opscode, Inc. Copyright: Copyright (C) 2010 Opscode, Inc License: 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. The Debian packaging is: Copyright (C) 2010 Opscode, Inc () and is licensed under the Apache 2.0 license. See "/usr/share/common-licenses/Apache-2.0" chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/control0000644000004100000410000000067312520074675024523 0ustar www-datawww-dataSource: chef-integration-test Section: ruby Priority: extra Maintainer: Joshua Timberman > Build-Depends: debhelper (>= 7.0.50~) Standards-Version: 3.8.4 Homepage: http://tickets.opscode.com Package: chef-integration-test Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Chef integration tests for APT in Cucumber This package is used for cucumber integration testing in Chef. chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/source/0000755000004100000410000000000012520074675024412 5ustar www-datawww-datachef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/source/format0000644000004100000410000000001412520074675025620 0ustar www-datawww-data3.0 (quilt) chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/changelog0000644000004100000410000000025212520074675024763 0ustar www-datawww-datachef-integration-test (1.0-1) unstable; urgency=low * Initial release (Closes: #CHEF-1718) -- Joshua Timberman Thu, 30 Sep 2010 09:53:45 -0600 chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/compat0000644000004100000410000000000212520074675024310 0ustar www-datawww-data7 chef-12.3.0/spec/data/apt/chef-integration-test-1.0/debian/rules0000755000004100000410000000067212520074675024177 0ustar www-datawww-data#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ chef-12.3.0/spec/data/apt/chef-integration-test_1.1.orig.tar.gz0000644000004100000410000000035512520074675024044 0ustar www-datawww-dataL a o`ivEajEpD1jE=0ƍDjFJ܎H*LPE¸`S}v\Yw4}]?S쀯D µ6Q{ugEy[>) bAE}?)kIǚqK|)7+lmoҤoڝv*(chef-12.3.0/spec/data/apt/chef-integration-test_1.0.orig.tar.gz0000644000004100000410000000035512520074675024043 0ustar www-datawww-dataL a o`ivEajEpD1jE=0ƍDjFJ܎H*LPE¸`S}v\Yw4}]?S쀯D µ6Q{ugEy[>) bAE}?)kIǚqK|)7+lmoҤoڝv*(chef-12.3.0/spec/data/apt/chef-integration-test_1.0-1_amd64.deb0000644000004100000410000000322012520074675023554 0ustar www-datawww-data! debian-binary 1285863052 0 0 100644 4 ` 2.0 control.tar.gz 1285863052 0 0 100644 468 ` n0`~68$hVۤI0]d~zT'!mi\\UB,fge쯪LH`A:Jgm۸O٠8/c/e@ )VovC]{ Veveq+K1rL':Xմ%@#drShRv::$"=a .8rkI(;oˏ|CMYu7T]".gg \M?YϒЀH߽WVCpsC>1 Ճy N2nndu&k ߂`Z6އ0YzӳrfzsHHO |&x:=T?dn IWB!B!B!Bھ7(data.tar.gz 1285863052 0 0 100644 1020 ` _Ec * R?G^L Y܉k.9 N6mv;^O}Q8ŗ""B/'"ȩ (g7rV'e2;7Z -+φQ \6 S<X3"\D);OQE-,CֶFհֿKmko);c7B`cƵQ_WjUuw<ا:;{__T6ߴ_HMӬ.:q{6n'(;o}Br?u%3`_\]gc0vyb|Oܻ4\y⅟ μ=_oa_[9ҫ3*YzsG]zr#/|'ݏ]ْvFnNr q:^]b ;SU !A B#w0( x2/F&(>9=y̡]:?9 4C `?u?2a'*U]Wِ>bѨ]W%GUj PCQ ͇.AG= 5+%x GLT; ?\|,h!fX8#>|!H<e ݨCC'ÑGzK@|y8iI(V+Ӎx`?1DB:"J%@#@niID8 0㉸2?]"D!o8D đ Z`ֲ[Ds j33F۞nAsƔݶ 1{jCnLca %QZoGA;G]ƢpGq IX**!v3?>]kDlg #YAa]o胢(bM0-:-4[Y{xMR|I$D"H$D"H$WԨ(chef-12.3.0/spec/data/lwrp/0000755000004100000410000000000012520074675015371 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp/resources/0000755000004100000410000000000012520074675017403 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp/resources/foo.rb0000644000004100000410000000015212520074675020511 0ustar www-datawww-dataactions :prepare_thumbs, :twiddle_thumbs default_action :pass_buck attribute :monkey, :kind_of => String chef-12.3.0/spec/data/lwrp/resources/bar.rb0000644000004100000410000000021012520074675020465 0ustar www-datawww-dataprovides :lwrp_bar # This makes sure that we cover the case of lwrps using provides actions :pass_buck, :prepare_eyes, :watch_paint_dry chef-12.3.0/spec/data/lwrp/providers/0000755000004100000410000000000012520074675017406 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb0000644000004100000410000000104612520074675030533 0ustar www-datawww-data# This action tests that embedded Resources have access to the enclosing Provider's # lexical scope (as demonstrated by the call to new_resource) and that all parameters # are passed properly (as demonstrated by the call to generate_new_name). attr_reader :enclosed_resource action :twiddle_thumbs do @enclosed_resource = lwrp_foo :foo do monkey generate_new_name(new_resource.monkey){ 'the monkey' } action :twiddle_thumbs provider :lwrp_monkey_name_printer end end def generate_new_name(str, &block) "#{str}, #{block.call}" end chef-12.3.0/spec/data/lwrp/providers/buck_passer.rb0000644000004100000410000000036412520074675022237 0ustar www-datawww-dataprovides :buck_passer action :pass_buck do lwrp_foo :prepared_thumbs do action :prepare_thumbs provider :lwrp_thumb_twiddler end lwrp_foo :twiddled_thumbs do action :twiddle_thumbs provider :lwrp_thumb_twiddler end end chef-12.3.0/spec/data/lwrp/providers/paint_drying_watcher.rb0000644000004100000410000000015112520074675024134 0ustar www-datawww-dataaction :prepare_eyes do "Prepared" end action :watch_paint_dry do "This isn't very interesting" end chef-12.3.0/spec/data/lwrp/providers/inline_compiler.rb0000644000004100000410000000067012520074675023106 0ustar www-datawww-data use_inline_resources action :test do ruby_block "interior-ruby-block-1" do block do # doesn't need to do anything end notifies :run, "ruby_block[interior-ruby-block-2]", :immediately end ruby_block "interior-ruby-block-2" do block do $interior_ruby_block_2 = "executed" end action :nothing end end action :no_updates do ruby_block "no-action" do block {} action :nothing end end chef-12.3.0/spec/data/lwrp/providers/monkey_name_printer.rb0000644000004100000410000000017012520074675023776 0ustar www-datawww-dataattr_reader :monkey_name action :twiddle_thumbs do @monkey_name = "my monkey's name is '#{new_resource.monkey}'" end chef-12.3.0/spec/data/lwrp/providers/buck_passer_2.rb0000644000004100000410000000035212520074675022455 0ustar www-datawww-dataaction :pass_buck do lwrp_bar :prepared_eyes do action :prepare_eyes provider :lwrp_paint_drying_watcher end lwrp_bar :dried_paint_watched do action :watch_paint_dry provider :lwrp_paint_drying_watcher end end chef-12.3.0/spec/data/lwrp/providers/thumb_twiddler.rb0000644000004100000410000000013612520074675022750 0ustar www-datawww-dataaction :prepare_thumbs do "Prepared" end action :twiddle_thumbs do "Twiddle twiddle" end chef-12.3.0/spec/data/lwrp/resources_with_default_attributes/0000755000004100000410000000000012520074675024410 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb0000644000004100000410000000011012520074675026545 0ustar www-datawww-dataattribute :penguin, :kind_of => String, :default => node[:penguin_name] chef-12.3.0/spec/data/fileedit/0000755000004100000410000000000012520074675016172 5ustar www-datawww-datachef-12.3.0/spec/data/fileedit/blank0000644000004100000410000000000012520074675017172 0ustar www-datawww-datachef-12.3.0/spec/data/fileedit/hosts0000644000004100000410000000015412520074675017255 0ustar www-datawww-data127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost chef-12.3.0/spec/data/nested.json0000644000004100000410000000375012520074675016567 0ustar www-datawww-data{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"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" }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} chef-12.3.0/spec/data/old_home_dir/0000755000004100000410000000000012520074675017031 5ustar www-datawww-datachef-12.3.0/spec/data/old_home_dir/my-dot-emacs0000644000004100000410000000000012520074675021241 0ustar www-datawww-datachef-12.3.0/spec/data/old_home_dir/my-dot-vim0000644000004100000410000000000012520074675020744 0ustar www-datawww-datachef-12.3.0/spec/data/incomplete-metadata-chef-repo/0000755000004100000410000000000012520074675022170 5ustar www-datawww-datachef-12.3.0/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/0000755000004100000410000000000012520074675026105 5ustar www-datawww-datachef-12.3.0/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb0000644000004100000410000000061012520074675030207 0ustar www-datawww-data# ## INCOMPLETE METADATA ## # This cookbook is invalid b/c it does not set the `name' of the cookbook. # Commented out for illustrative purposes # name 'incomplete-metadata' maintainer '' maintainer_email '' license '' description 'Installs/Configures incomplete-metadata' long_description 'Installs/Configures incomplete-metadata' version '0.1.0' chef-12.3.0/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md0000644000004100000410000000010312520074675027356 0ustar www-datawww-data# incomplete-metadata TODO: Enter the cookbook description here. chef-12.3.0/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/0000755000004100000410000000000012520074675027537 5ustar www-datawww-datachef-12.3.0/spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb0000644000004100000410000000013212520074675031504 0ustar www-datawww-data# # Cookbook Name:: incomplete-metadata # Recipe:: default # # Copyright (C) 2014 # # # chef-12.3.0/spec/data/config.rb0000644000004100000410000000013712520074675016200 0ustar www-datawww-data# # Sample Chef Config File # cookbook_path "/etc/chef/cookbook", "/etc/chef/site-cookbook" chef-12.3.0/spec/data/object_loader/0000755000004100000410000000000012520074675017201 5ustar www-datawww-datachef-12.3.0/spec/data/object_loader/nodes/0000755000004100000410000000000012520074675020311 5ustar www-datawww-datachef-12.3.0/spec/data/object_loader/nodes/test.json0000644000004100000410000000025112520074675022161 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "environment": "prod", "run_list": [] } chef-12.3.0/spec/data/object_loader/nodes/test.rb0000644000004100000410000000003712520074675021615 0ustar www-datawww-dataname "test" environment "prod" chef-12.3.0/spec/data/object_loader/nodes/test_json_class.json0000644000004100000410000000030712520074675024401 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "json_class": "Chef::Node", "environment": "prod", "run_list": [] } chef-12.3.0/spec/data/object_loader/roles/0000755000004100000410000000000012520074675020325 5ustar www-datawww-datachef-12.3.0/spec/data/object_loader/roles/test.json0000644000004100000410000000025112520074675022175 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "description": "prod", "run_list": [] } chef-12.3.0/spec/data/object_loader/roles/test.rb0000644000004100000410000000003712520074675021631 0ustar www-datawww-dataname "test" description "prod" chef-12.3.0/spec/data/object_loader/roles/test_json_class.json0000644000004100000410000000030712520074675024415 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "json_class": "Chef::Role", "description": "prod", "run_list": [] } chef-12.3.0/spec/data/object_loader/environments/0000755000004100000410000000000012520074675021730 5ustar www-datawww-datachef-12.3.0/spec/data/object_loader/environments/test.json0000644000004100000410000000025112520074675023600 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "description": "prod", "run_list": [] } chef-12.3.0/spec/data/object_loader/environments/test.rb0000644000004100000410000000003712520074675023234 0ustar www-datawww-dataname "test" description "prod" chef-12.3.0/spec/data/object_loader/environments/test_json_class.json0000644000004100000410000000031612520074675026020 0ustar www-datawww-data{ /* testing that we support c-style comments */ // testing that we support c++-style comments as well "name": "test", "json_class": "Chef::Environment", "description": "prod", "run_list": [] } chef-12.3.0/spec/data/trusted_certs/0000755000004100000410000000000012520074675017277 5ustar www-datawww-datachef-12.3.0/spec/data/trusted_certs/opscode.pem0000644000004100000410000000713012520074675021437 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIFrDCCBJSgAwIBAgIQB1O/fCb6cEytJ4BP3HTbCTANBgkqhkiG9w0BAQUFADBI MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSIwIAYDVQQDExlE aWdpQ2VydCBTZWN1cmUgU2VydmVyIENBMB4XDTE0MDYxMDAwMDAwMFoXDTE1MDcw MTEyMDAwMFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO BgNVBAcTB1NlYXR0bGUxGzAZBgNVBAoTEkNoZWYgU29mdHdhcmUsIEluYzEWMBQG A1UEAwwNKi5vcHNjb2RlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAMm+rf2RcPGBlZoM+hI4BxlaHbdRg1GZJ/T46UWFOBnZFVP++TX/pyjDsvns xymcQywtoN/26+UIys6oWX1um9ikEokvf67LdsUeemQGFHFky8X1Ka2hVtKnxBhi XZfvyHDR4IyFWU9AwmhnqySzxqCtynUu8Gktx7JVfqbRFMZ186pDcSw8LoaqjTVG SzO7eNH2sM3doMueAHj7ITc2wUzmfa0Pdh+K8UoCn/HopU5LzycziJVPYvUkLT2m YCV7VWRc+kObZseHhZAbyaDk3RgPQ/eRMhytAgbruBHWDqNesNw+ZA70w856Oj2Y geO7JF+5V6WvkywrF8vydaoM2l8CAwEAAaOCAm8wggJrMB8GA1UdIwQYMBaAFJBx 2zfrc8jv3NUeErY0uitaoKaSMB0GA1UdDgQWBBQK5zjZwbcmcMNLnI2h1ioAldEV ujCBygYDVR0RBIHCMIG/gg0qLm9wc2NvZGUuY29tghBjb3JwLm9wc2NvZGUuY29t ghIqLmNvcnAub3BzY29kZS5jb22CDyoubGVhcm5jaGVmLmNvbYISKi5jb3JwLmdl dGNoZWYuY29tgg0qLmdldGNoZWYuY29tggwqLm9wc2NvZGUudXOCC2dldGNoZWYu Y29tggtvcHNjb2RlLmNvbYIRYXBpLmJlcmtzaGVsZi5jb22CDWxlYXJuY2hlZi5j b22CCm9wc2NvZGUudXMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF BwMBBggrBgEFBQcDAjBhBgNVHR8EWjBYMCqgKKAmhiRodHRwOi8vY3JsMy5kaWdp Y2VydC5jb20vc3NjYS1nNi5jcmwwKqAooCaGJGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 LmNvbS9zc2NhLWc2LmNybDBCBgNVHSAEOzA5MDcGCWCGSAGG/WwBATAqMCgGCCsG AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMHgGCCsGAQUFBwEB BGwwajAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGCCsG AQUFBzAChjZodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTZWN1 cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQUFAAOCAQEA kgBpJ2t+St7SmWfeNU9EWAhy0NuUnRIi1jnqXdapfPmS6V/M0i2wP/p+crMty78e +3ieuF5s0GJBLs85Hikcl3SlrrbIBJxozov1TY6zeOi6+TCsdXer6t6iQKz36zno +k+T6lnMCyo9+pk1PhcAWyfo1Fz4xVOBVec/71VovFkkGD2//KB+sbDs+yh21N9M ReO7duj16rQSctfO9R2h65djBNlgz6hXY2nlw8/x3uFfZobXOxDrTcH6Z8HIslkE MiTXGix6zdqJaFRCWi+prnAztWs+jEy+v95VSEHPj3xpwZ9WjsxQN0kFA2EX61v/ kGunmyhehGjblQRt7bpyiA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx 8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6 Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M 2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24 LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh v+PMGxmcJcqnBrJT3yOyzxIZow== -----END CERTIFICATE----- chef-12.3.0/spec/data/trusted_certs/root.pem0000644000004100000410000000247212520074675020772 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- chef-12.3.0/spec/data/trusted_certs/intermediate.pem0000644000004100000410000000315312520074675022456 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx 8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6 Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M 2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24 LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh v+PMGxmcJcqnBrJT3yOyzxIZow== -----END CERTIFICATE----- chef-12.3.0/spec/data/trusted_certs/example.crt0000644000004100000410000000242212520074675021444 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDkjCCAnoCCQDihI8kxGYTFTANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMRAwDgYDVQQKEwdZb3VD b3JwMRMwEQYDVQQLEwpPcGVyYXRpb25zMRYwFAYDVQQDEw1leGFtcGxlLmxvY2Fs MR0wGwYJKoZIhvcNAQkBFg5tZUBleGFtcGxlLmNvbTAeFw0xMzEwMTcxODAxMzVa Fw0yMzEwMTUxODAxMzVaMIGKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAO BgNVBAcTB1NlYXR0bGUxEDAOBgNVBAoTB1lvdUNvcnAxEzARBgNVBAsTCk9wZXJh dGlvbnMxFjAUBgNVBAMTDWV4YW1wbGUubG9jYWwxHTAbBgkqhkiG9w0BCQEWDm1l QGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyKBo U+Bdni0xZK/NCzdLdi2X+TyW5eahbYMx+r1GDcVqCICvrthBCVLVFsQ8rvOHwTPi AxQJGxb9TLSXRgXQSlH6FLjIUceuOtpan3qYVJ1v7AxY4DgNvYBpbtJz5MQedJnT g2F+rXzkwaD6CWBqWHeGU0oP3r7bq1AMD6XEsK2w2/zHtG7TEnL45ARv1PsyrU5M ZAW/XyoMyq1k2Lpv7YR5kAvTq1+4RSt/it2RFE7R0AVbaQ0MeAnllfySiHHHlaOT FVd/qPSiGISxsUmmzA3Z08+0sfJwkrnJXbLscCBYndd7gMGgtczGjJtul0Ch3GFa /Pn5McjwF272+usJ1wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCzPePWifWNECsG nL8on1AtFMkczE1/pdRS4YUl/Tc926MpezptSja8rL31+4Bom37/wYPG7HygtAQl R4FHpAtuqJKPOfjUmDNsIXRFnytrnflTpctDu/Nbj4PDCy01k/sTDUQt+s+lEBL8 M8ArmfLZ8PCfAwnXmJQ5rggDFKqegjt6z1RsSglbMiASE7+KkpBnzaqH6fET6IQz WgAjv6WdRfwgfJjOTSX4XMpCSet9KaWmXExKrxiVng2Uu6E+ShVAyKaGMuc1B7VA oxnnVaVapFv5lOWucQr4KkC7EgaUZnyt8duOc8+Yvd+y3Xd2dcHUnmegRxly4jRV /lXbFAUb -----END CERTIFICATE----- chef-12.3.0/spec/data/mac_users/0000755000004100000410000000000012520074675016366 5ustar www-datawww-datachef-12.3.0/spec/data/mac_users/10.7.shadow.xml0000644000004100000410000000050712520074675020763 0ustar www-datawww-data SALTED-SHA512 b3XXGQRB+sw0KR676h/HVrJC1P6bz/FBvMuE8ZeeJ+U5U5qjH599zJLAzqlZ6hjhi3IO NY5/vjz76qVhRW9roAiTejA= chef-12.3.0/spec/data/mac_users/10.7.plist.xml0000644000004100000410000007547712520074675020653 0ustar www-datawww-data KerberosKeys MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCCBdluECwTg7Fe5bsZ+ kxWTdvLPPtNGBCZOK2+aEFrkBaJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuREZG QTkxRjM1QjUxNjMzMDNDMDc5RTk5ODc2NDAzMEQwOTU2QUYyNnZhZ3JhbnQw Y6EbMBmgAwIBEaESBBAHZXv8koch6fiOdgRkDXyjokQwQqADAgEDoTsEOUxL REM6U0hBMS5ERkZBOTFGMzVCNTE2MzMwM0MwNzlFOTk4NzY0MDMwRDA5NTZB RjI2dmFncmFudDBroSMwIaADAgEQoRoEGKs+5dPs07zLf/0Vhu+YWCXZ6iwg NLpkqKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuREZGQTkxRjM1QjUxNjMzMDND MDc5RTk5ODc2NDAzMEQwOTU2QUYyNnZhZ3JhbnQ= ShadowHashData YnBsaXN0MDDRAQJdU0FMVEVELVNIQTUxMk8QRG911xkEQfrMNCkeu+ofx1ay QtT+m8/xQbzLhPGXniflOVOaox+ffcySwM6pWeoY4YtyDjWOf748++qlYUVv a6AIk3owCAsZAAAAAAAAAQEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAGA= _writers_LinkedIdentity vagrant _writers_hint vagrant _writers_jpegphoto vagrant _writers_passwd vagrant _writers_picture vagrant authentication_authority ;ShadowHash;HASHLIST:<SALTED-SHA512> ;Kerberosv5;;vagrant@LKDC:SHA1.DFFA91F35B5163303C079E998764030D0956AF26;LKDC:SHA1.DFFA91F35B5163303C079E998764030D0956AF26 generateduid 11112222-3333-4444-AAAA-BBBBCCCCDDDD gid 80 home /Users/vagrant jpegphoto /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4 mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7 j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8 zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3 XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/ x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/ pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2 JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2 a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9 yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2 x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1 gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3 i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8 KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9 Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+ XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7 PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6 INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51 H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG// ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4 vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+ Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7 J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5 EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9 ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7 x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7 iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/ X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6 jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0 vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+ 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/ 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79 Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/ 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+ 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5 MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8 mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+ BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2 PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+ dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+ vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/ AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3 xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/ SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5 J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/ ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1 zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3 XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9 x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+ Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+ NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/ 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6 +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9 F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4 /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/ mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6 D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/ m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4 hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/ 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63 hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9 ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0 HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3 OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1 JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40 rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3 cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0 VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+ 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0 iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6 /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1 /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+ Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0 m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0 uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9 piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/ xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q== name vagrant passwd ******** passwordpolicyoptions PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8 L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp Y3Q+CjwvcGxpc3Q+Cg== realname vagrant shell /bin/bash uid 501 chef-12.3.0/spec/data/mac_users/10.8.plist.xml0000644000004100000410000007570012520074675020641 0ustar www-datawww-data KerberosKeys MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCBxHUxawMNiov49kfZn M38ddgXFivE9SNpYgPamy+6prKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVB OUJFMDZCQzExQjAxODdEMzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQw Y6EbMBmgAwIBEaESBBBD9mGbvFTNIUKAvAbnjh8ookQwQqADAgEDoTsEOUxL REM6U0hBMS40RUE5QkUwNkJDMTFCMDE4N0QzNDUyQjdBMDk2MTdCMEI5MjY5 NjhFdmFncmFudDBroSMwIaADAgEQoRoEGG4TEFIf416UH7MvFW7sAXC8ArC6 AhbCraJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVBOUJFMDZCQzExQjAxODdE MzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQ= ShadowHashData YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50 cm9weVRzYWx0Wml0ZXJhdGlvbnNPEIDqTC0mXYAboOwN/M0lPfwd6Ry+CAa0 rMHtf+Iq689r61NE0PRC5ZD/oE1nkHXaOvsRnkG3K16vCO5KpUaTciZG1Rnu BIQ964o+l3Qo0z9iXoOIeRPlwTtwA1lhXgCte8PnoMmK/D4Z0TYCckVPjTOp IU0vvovmjR+YIbJmiTEjZk8QIPmU7y9zt8VZTr0VUzAJdrIHM84OJNZZeD2H 89gcu7apEZugCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA AAAAAOo= _writers_hint vagrant _writers_jpegphoto vagrant _writers_passwd vagrant _writers_picture vagrant authentication_authority ;ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2> ;Kerberosv5;;vagrant@LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E;LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E generateduid 11112222-3333-4444-AAAA-BBBBCCCCDDDD gid 80 home /Users/vagrant jpegphoto /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4 mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7 j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8 zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3 XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/ x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/ pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2 JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2 a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9 yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2 x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1 gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3 i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8 KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9 Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+ XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7 PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6 INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51 H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG// ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4 vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+ Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7 J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5 EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9 ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7 x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7 iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/ X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6 jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0 vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+ 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/ 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79 Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/ 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+ 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5 MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8 mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+ BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2 PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+ dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+ vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/ AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3 xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/ SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5 J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/ ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1 zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3 XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9 x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+ Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+ NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/ 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6 +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9 F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4 /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/ mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6 D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/ m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4 hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/ 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63 hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9 ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0 HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3 OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1 JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40 rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3 cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0 VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+ 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0 iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6 /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1 /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+ Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0 m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0 uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9 piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/ xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q== name vagrant passwd ******** passwordpolicyoptions PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8 L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp Y3Q+CjwvcGxpc3Q+Cg== realname vagrant shell /bin/bash uid 501 chef-12.3.0/spec/data/mac_users/10.9.plist.xml0000644000004100000410000007603312520074675020642 0ustar www-datawww-data KerberosKeys MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCAJxcIcjX3sMb98++d0 YvKqc351+CJJTMpyJO5mwWFMCaJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEEy NDA4NDVBMjU0OUZCOEUwRjI4NEU1NkUyODE3NzU2RUU5Q0QyMnZhZ3JhbnQw Y6EbMBmgAwIBEaESBBDzYvuM3CLsLOGCIX4FJ8vdokQwQqADAgEDoTsEOUxL REM6U0hBMS40QTI0MDg0NUEyNTQ5RkI4RTBGMjg0RTU2RTI4MTc3NTZFRTlD RDIydmFncmFudDBroSMwIaADAgEQoRoEGCkvuVvN92vqnm0cy+9GWNBoIEoW XtUNx6JEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEEyNDA4NDVBMjU0OUZCOEUw RjI4NEU1NkUyODE3NzU2RUU5Q0QyMnZhZ3JhbnQ= ShadowHashData YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50 cm9weVRzYWx0Wml0ZXJhdGlvbnNPEIASYBqQ2xfL+LpICOY4L7DTudimwaGQ R3v2gKshr7YGVGcTblXMIIpvdBVuPa8g+xM2nvS3uvoEfYA1n7RqSKStzNVI 67M4UbCTR8yoQ0Gn+TonFHND+J+4Q/tGwAF9Jkr6SXa6rPlBuRW9HsHKJMML PnWeAkA+AvWf5/9ZOKdjbE8QIO6VS+Ry/cYN34lIR4FDOZNiXwBq9uyBDAj0 mn5BOUahEYayCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA AAAAAOo= _writers_hint vagrant _writers_jpegphoto vagrant _writers_passwd vagrant _writers_picture vagrant authentication_authority ;ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2> ;Kerberosv5;;vagrant@LKDC:SHA1.4A240845A2549FB8E0F284E56E2817756EE9CD22;LKDC:SHA1.4A240845A2549FB8E0F284E56E2817756EE9CD22 generateduid 11112222-3333-4444-AAAA-BBBBCCCCDDDD gid 80 home /Users/vagrant jpegphoto /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4 mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7 j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8 zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3 XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/ x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/ pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2 JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2 a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9 yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2 x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1 gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3 i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8 KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9 Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+ XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7 PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6 INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51 H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG// ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4 vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+ Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7 J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5 EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9 ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7 x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7 iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/ X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6 jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0 vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+ 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/ 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79 Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/ 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+ 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5 MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8 mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+ BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2 PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+ dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+ vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/ AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3 xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/ SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5 J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/ ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1 zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3 XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9 x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+ Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+ NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/ 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6 +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9 F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4 /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/ mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6 D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/ m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4 hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/ 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63 hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9 ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0 HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3 OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1 JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40 rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3 cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0 VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+ 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0 iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6 /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1 /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+ Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0 m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0 uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9 piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/ xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q== name vagrant passwd ******** passwordpolicyoptions PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8 L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtl eT5wYXNzd29yZExhc3RTZXRUaW1lPC9rZXk+Cgk8ZGF0ZT4yMDE0LTAzLTA2 VDE4OjU0OjQ1WjwvZGF0ZT4KPC9kaWN0Pgo8L3BsaXN0Pgo= realname vagrant shell /bin/bash uid 501 chef-12.3.0/spec/data/mac_users/10.8.shadow.xml0000644000004100000410000000141712520074675020765 0ustar www-datawww-data SALTED-SHA512-PBKDF2 entropy 6kwtJl2AG6DsDfzNJT38HekcvggGtKzB7X/iKuvPa+tTRND0QuWQ/6BNZ5B1 2jr7EZ5BtyterwjuSqVGk3ImRtUZ7gSEPeuKPpd0KNM/Yl6DiHkT5cE7cANZ YV4ArXvD56DJivw+GdE2AnJFT40zqSFNL76L5o0fmCGyZokxI2Y= iterations 39840 salt +ZTvL3O3xVlOvRVTMAl2sgczzg4k1ll4PYfz2By7tqk= chef-12.3.0/spec/data/mac_users/10.9.shadow.xml0000644000004100000410000000141712520074675020766 0ustar www-datawww-data SALTED-SHA512-PBKDF2 entropy EmAakNsXy/i6SAjmOC+w07nYpsGhkEd79oCrIa+2BlRnE25VzCCKb3QVbj2v IPsTNp70t7r6BH2ANZ+0akikrczVSOuzOFGwk0fMqENBp/k6JxRzQ/ifuEP7 RsABfSZK+kl2uqz5QbkVvR7ByiTDCz51ngJAPgL1n+f/WTinY2w= iterations 34482 salt 7pVL5HL9xg3fiUhHgUM5k2JfAGr27IEMCPSafkE5RqE= chef-12.3.0/spec/data/mac_users/10.7-8.shadow.xml0000644000004100000410000000050712520074675021130 0ustar www-datawww-data SALTED-SHA512 b3XXGQRB+sw0KR676h/HVrJC1P6bz/FBvMuE8ZeeJ+U5U5qjH599zJLAzqlZ6hjhi3IO NY5/vjz76qVhRW9roAiTejA= chef-12.3.0/spec/data/mac_users/10.7-8.plist.xml0000644000004100000410000007570012520074675021005 0ustar www-datawww-data KerberosKeys MIIBVKEDAgEBoIIBSzCCAUcwc6ErMCmgAwIBEqEiBCBxHUxawMNiov49kfZn M38ddgXFivE9SNpYgPamy+6prKJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVB OUJFMDZCQzExQjAxODdEMzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQw Y6EbMBmgAwIBEaESBBBD9mGbvFTNIUKAvAbnjh8ookQwQqADAgEDoTsEOUxL REM6U0hBMS40RUE5QkUwNkJDMTFCMDE4N0QzNDUyQjdBMDk2MTdCMEI5MjY5 NjhFdmFncmFudDBroSMwIaADAgEQoRoEGG4TEFIf416UH7MvFW7sAXC8ArC6 AhbCraJEMEKgAwIBA6E7BDlMS0RDOlNIQTEuNEVBOUJFMDZCQzExQjAxODdE MzQ1MkI3QTA5NjE3QjBCOTI2OTY4RXZhZ3JhbnQ= ShadowHashData YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhXZW50 cm9weVRzYWx0Wml0ZXJhdGlvbnNPEIDqTC0mXYAboOwN/M0lPfwd6Ry+CAa0 rMHtf+Iq689r61NE0PRC5ZD/oE1nkHXaOvsRnkG3K16vCO5KpUaTciZG1Rnu BIQ964o+l3Qo0z9iXoOIeRPlwTtwA1lhXgCte8PnoMmK/D4Z0TYCckVPjTOp IU0vvovmjR+YIbJmiTEjZk8QIPmU7y9zt8VZTr0VUzAJdrIHM84OJNZZeD2H 89gcu7apEZugCAsiKTE2QcTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA AAAAAOo= _writers_hint vagrant _writers_jpegphoto vagrant _writers_passwd vagrant _writers_picture vagrant authentication_authority ;ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2> ;Kerberosv5;;vagrant@LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E;LKDC:SHA1.4EA9BE06BC11B0187D3452B7A09617B0B926968E generateduid 11112222-3333-4444-AAAA-BBBBCCCCDDDD gid 80 home /Users/vagrant jpegphoto /9j/4AAQSkZJRgABAQAAAQABAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBw bAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBs AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAA AG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAH MAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFk AAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRH ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJv ZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAo AAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUA AAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtv S1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAAD Hmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAa AAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwA AAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZp RkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE 8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAg AFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcA QgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDo AHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8E FwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUABy AG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIA RwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBl AHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkA bMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAg AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEA bABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBs AHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIA RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBv AGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACAD wAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBl AG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8A ZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBH AGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUA bgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBs AG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4E RAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYn BkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwA ZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABz AGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdo dHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRN AAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8A ALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA /ZH///ui///9owAAA9wAAMBs/9sAQwACAgICAgECAgICAgICAwMGBAMDAwMH BQUEBggHCAgIBwgICQoNCwkJDAoICAsPCwwNDg4ODgkLEBEPDhENDg4O/9sA QwECAgIDAwMGBAQGDgkICQ4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O Dg4ODg4ODg4ODg4ODg4ODg4ODg4O/8AAEQgBMQEuAwEiAAIRAQMRAf/EAB8A AAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQE AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYX GBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfI ycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEB AQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMR BAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJico KSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWG h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW 19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/fyiis6/1Wz0 6P8AfyAyY+WJeWP4dvxoA0aw9R1+zsN0at9puR/Ah4H1NcjqHiG9vQ0cZ+yQ H+FD8x+p/wAK5+gDWu9a1C8u1kad4gpyiRkqF/z71u6d4pI2xaiu4dPOQc/i P8K43v0P5UlMZ7LDPDcW6ywSJLGejKcipa8gtL65sLjzLaV4z3HVT9RXb6d4 mt7grFegW0398fcP+H40hHUUUgIZQykEEZBHeloAKKKKACiiigAooooAKKKK ACiiigAooooAKKKKACiiigAooooAKKKguLmC1tzLcSpDGO7H/OaAJ6K4y48W gXyi2tvMtwfmLnDN9PSuisNVs9RjzBJiQfeibhh+Hf8ACgC3cgHT5wQCDG2Q fpX4u/CD9rf4h/C+aHRtXkfxt4PjbaLG/mP2i2XP/LGc5YADorbl7AL1r9o7 j/jxn/65n+VfzYS/8fMn+8f51+qeG+W4XHUcXSxEFKPub/8Ab2z3T9D8p8S8 zxWBq4Srh5uMvf2/7d3WzXkz99/hb8cPh38XtCW48J6yn9ppGGutHvMRXtv6 5TJ3L/tIWX3zxXrtfgD8JPhh8TviF4ut9Q+Hlvd6ZDZTgv4nluHtLSxYd1mX 5nkGfuRbm+lftD4C1HXfDvw+0vRfGHiW48batBFtudbeyS2aZvaNf4QOAxO4 9W5r57jHh7BZXX5cPXUr/Z3lH1a0++z8nufQ8G8R43NaHNiKDjb7X2Zeiev3 XXpseu0VBb3MF1biW3lSaM91NT18Yfankuk/FDQfHPgxNb8B6zYa1ojna17a ybnjb+5Ihw0T/wCy4Bqkzs8hd2Z3JyWJyTX4a+HPF3ijwL4/bxB4O13UPDus K5DTWr/LMufuSocrKn+y4Ir76+Fv7YXhzXfs+j/FO1tvB+sHCLrtorNplwfW VOXtifX5o/da/Rc/8O8bg71cL+9h2+0vl19Vr5I/OOHvEbBYxqli/wB1Pv8A Zfz6fPTzPs6jt61HFLDPY293bTwXVpcIJLe4t5VlimQ9GR1JVgfUE0/mvzux +jppq6F70lL6elHagBPyo7elHejvQM1tP1m905wI3MkPeJ+V/D0ru9O1yy1D CK3k3B/5ZOeT9D3ry/tR0YH360CPaaK8607xJd2m2K6zdQDjJPzj6Hv+NdzZ 39rfweZbSq/95Twy/UUgLlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABS MyohZ2CqBkknAFYmo69Z2AZFb7Rcj/lmh6fU9q4S/wBWvNRc+dJiLPyxJwo/ x/GgDrNR8TwQho7BRcydPMP3B/jXE3V3c3lwZrmV5X9+g+g7VXzj2PrS8EZP FMBvtT1ZkkDozI68gqcEU0gjP86Tv9aBnWWPiWXyWttQ/eIyFRMo+YccZHQ/ pXwD8MP2PdC0iSDWvivd2/inVc+Yvh+xkZdOgPXE0gw9wR3Vdqf71fadHQ5r 1MvzvG4GlUp4efKp2u1vpfZ7rfpqeRmGR4LHVadTEQ5nC9k9tbbrrt10IoIY LTTbaxs7e2srC2Ty7a1toVihgX+6iKAqj2AqXtSVla7r2ieFvDjav4j1S00f TRwstw3Mp/uxqPmkb2UH8K86EJ1JqMVeT6LVtnpznClByk0orvokv0N62u7i yuRLbTPE4646H6jvV3U/i74Q8M3MVh4p1SGw1Z03rZwRvNMU/vtGgLIuSOWx nPFfEXjj9ojWNT87TvAcE/hzTzlW1S4UG+lHqi8rAD6/M/uK8d8JPJL4xvbi eSWe5lgZ5ZpXLySMWXJZjyT7k1+g5XwBXnTdbGPkX8q+L5vZfi/Q/PM08QKE aipYKPO/5n8PyWjfrovU+P5/+P2b/fP86irqfG/hfUvBXxe8SeE9XXGoaVqE ttK23Ak2sdrj/ZZcMPYiuWr97pVI1IRlF3TV0fz/AFacqc5QkrNOzPpr4GfE bxb4B8GyDQdQEmlm/czaReZks5OFzhc5jb/aQg/WvvvwJ8ZvCPjeSGwkk/4R rxG/A02/lGyZv+mE3Cv/ALp2t7GvzM8Af8iHL/1+yf8AoK128dvJeXcFlBbT XtzO4WG2hiMkkrdgqgEk/SviOIuE8BmUpTkuSf8AMv1Wz+evmj7vhzizH5bC MIPmh/K/0e6+Wnkz9V2VkdlYMrDhlIwRTfzr5Ut/E3xQ+CX7Nlz4x8foniHQ bW7tbW38O3FyDqduk0mzd9p5CbRyIn389Ste2fDz4peBPipob3ngvW0vbmJN 15pNyvk6hZ/9dISclf8AbQsnvX4hj8jr4eMqsGqlJO3PHWN9N+268uibP3DA Z/h8TONKadOq1fklpK3l32fn3SO/780d6Xr3/Wkrxj3Be1JS/likoAU9KfFL LBOssMjxSA5DKcGmUn50Adrp3in7sOornt5yD+Y/w/KuximingWWGRJY26Mp yDXjXOenWuL8V/Fzw58MWYahqMlzrRXcmiWJElxJ6GQH5Yl/2nwfQGt8LhK2 JqKnRi5SfRf1+OxzYrF0cNSdWtNRiur/AK/A+naK5vwdr0nij4VeHfEktqtl JqenxXTW6ybxEXUNt3YGcZxnFdJWVWnKnNwlutDWlUjUgpx2auvmFFFFQWFF FFABRRRQAUUV4D8evjfbfBHQ/Cmpajo17qmk6tqD2l3NYyL9otFEe4SJG3yy e67lPoc104PB1sVWVGjHmk9l30ucuMxlHCUXWrStFbv52PcL3UbTT4d1zKFY jKoOWb6CuG1LxHd3ZaODNrbnsp+Zvqf6CvOvCnjfwv8AETwqfEfhHxDZ+JNP JHnyRMfOt2P8E8bfPE3swx6E10fFZVaU6U3CpFqS3T0a9UbUa1OtBVKclKL2 a1T+Yd6KPyrk/F3jjwv4F0lbnxLqaWs0i7rawhXzbu5/3Igc4/2mwvvVUKFW tUVOlFyk9ktWLEYilQpupVkoxW7bsjq6O9fK+hftaeBrj4n3nhvxlpl34It9 yHT9Wkm+02xVlyFudq5ibP8AGoZPp1r6lhlgudOt7y0uLe8sbhBJbXVtKssM yf3kdSVYe4NduZZPjcvmo4mm4327P0aun566HDlmdYHMIuWGqKVt+69U7P8A Akyce1LwTx1pM0qqzyBEVmduiqMk15h6YnQ1HPNBa6bc315cW1lY2yeZc3Vz MsUMCf3ndiFUe5NeXeOvjH4R8DtLY+b/AMJJ4hQY/s2wlG2E/wDTaYZVP90Z b2Ffm58ZfGXxR+IOpvdeKdU/tDwvFIXtNJ0pDFY2Y7F4cku4/wCejlifUV9n w9wTjczanP8Ad0+73f8AhXX1dl2bPiuIuOMFlicIfvKnZbL1fT0V33sfUfxR /bE0LR/tOjfCmzt/FWqDKN4gv42GnQHpmGM4a4I7M21P96vmXR/FPiTxra3f iLxdrmoeItblvJAbq8kzsXC4SNRhY0HZVAFfP4IIyCCD0INexeAf+RCk/wCv 2T+S1+1Zbw1gMqo2w8Pe6yesn8+i8lZeR+JZjxNmGbV74ifu9IrSK+X6u78z ta6zwd/yM0//AF6t/wChLXJnpXt3wO+HWp/EHx1rENldR2FtZWAae5kjLKGe RQicdyFcj/dNGZ4inQws6lR2iupeWYepXxUKdNXk+g79vb4c/wBm/Erw78Tr CDbaaxENP1RlXgXMS5iYn1eIFfpBX58V+/nx0+HafFH9lzxX4TWJZNTktTca UxxlbqL54sHtuI2E/wB12r8BXR45njkVkkUkMrDBBHUEV4/h1m/1vLPYyfvU tPl9n9V8j1vEfJ/qmae2ivdq6/P7X6P5n1L+zt8Orj4lWd/pltr+j6StndNL eRvKJL7yyF+aK3zlx1+c4UY5r9FfBnw/8KeArBk8OacFvZE2z6pdESXk/wBX x8i/7KAD61+JVpd3mna1aalpt5eabqdrIJLW8tJmimgYfxI6kFT9DX218Lv2 x9V082+jfFuym16yGEXxJpsCi9jHTNxAMLOPV02v3IauDjnh/OcYnPDVOan1 gtH/APbej26Js7+A+IMmwbUMTT5anSb1X/2vqvm0j6A/a0/5MP8AEIH/AEGN N/8AR5r8p7G+vtK16z1XSr+90rVbSQSWt7ZTtDPA3qjqQR/XvX6f/tJ+I/D3 i3/gnHrOv+Fdc0zxFos+sad5d5Yzb1z5/KsPvIwyMqwBHpX5b11eGlJxympC as+eSaf+GOjX6HL4l1oyzenUpyuuSLTT85apo+5/hd+2TqFobfR/i9Yy6va8 KnibS7cC6QetxbjCy+7x7W7lWr700TW9F8TeErTX/Der6dr+h3IHkX1jMJIm OM7T3Vh3VgGHpX4SV2Hgjx/4y+HHis6z4L1670S7fi5iXD212v8Admhb5JB9 Rn0IrHiDw4wmKvVwbVOfb7L+X2flp5G3DviTi8JaljF7SHf7S+f2vnr5n7fY 70vavkv4XftbeDfF722j+PobbwB4kchFvDIW0q7Y8cOfmtyf7r5XPRhX1hcT QWelSajeXVraaakQle8mmVYAhHD+ZnaVPYg89s1+MZnk+My+t7LE03F9Oz9H s/kfteV51gsxo+1w9RSS37r1XQf+VYviHxLoHhLw7/aviXVrXSLM5ERlOZJz /djjHzOfoMepFfP3jj9ouxtPO074fW0eqXPKnWr6I/Zk94Yjgyf7z4X2NfKm r6vq3iDxFNq+u6le6xqkv37m7k3vj+6Oyr/sqAPavrcj4BxWKtUxX7uHb7T+ XT56+R8jnnH+Fw16eEXtJ9/sr5/a+Wnme7eOP2hde1kzad4Khn8LaU2Va/kw dQnHqpGVgB/2ct/tCvng5aaSRmeSWRi8kkjFndj1LMeSfc0UV+t5blOEy+n7 PDw5V17v1e7/AE6H5JmWbYvH1faYibk+nZei2X9XP1/+E3/JsXgD/sAWv/op a9Crz34S/wDJsPgD/sAWv/opa9Cr+ccx/wB7q/4n+Z/R+W/7pS/wr8kFFFFc R2hRRRQAUUUUAFfn7/wUE/5Ij4B/7Dkv/og1+gVfn7/wUE/5Ij4B/wCw5L/6 INfVcEf8jzD+r/JnynHH/IixHovzR+YfhzxJ4h8H+MbfxD4U1vUvDutw8JeW MuxyP7rjo6HurAg+lffXws/bF0rVWtdD+LFjFoOpMRHH4i0yBmspm6Dz4Blo ST/Em5OfurX511Ys/wDkNWP/AF8x/wDoYr98zvhvAZrC2Ih7y2ktJL5/o7ry P5+yPiTMMqqXw89OsXrF/L9VZ+Z+mPjr9oq7lln0z4fWr2EQJR9bv4QZ294Y TkJ7M+W7gCvmO6ubq/1a41C/urm/1Cdt091cymSWU+rMeT/Smy/8fMn+8f51 H+PNcGVZLg8up8mHhbu+r9X+m3ZHqZrnWMzGpz4id+y6L0X6792eJ+N/+SmX n/XCH/0Cul+Gnxj8ffCfUifCmrB9Hkk33WhX4M1hcep2ZzG3+3GVP1rmvG// ACUy8/64Q/8AoFcpX0VXCUcTh/ZVoKUWtU1dHzFLF1sNiPa0ZuMk9GnZn6se B/2q/hr4s8I3Fxq0eqeFfEdrDvn0UxG5+084JtpVADrnGQ+1lByc9a8s8d/H bxT4shuNO0USeEvD7gq0NtNm7uF/6azDoD/dTA9zXxj4A/5Hub/ryk/mtewZ z/8AWr43DcFZVgsS6sIXe6UndR9P83d+Z9riONs2x2FVKpUstnZWcvX/ACVl 3QgAVcAADPQU4Eq2VJU+opMcUV9KfOHJa14O0vVWknt9ulX7cmSJP3ch/wBt P6rg/WrHhTS7zR/DM1jfJGswu3ZTG+5XUhcMD+B6810tFW6knHlZkqUVLmW4 vb0r9RP2cPBX/CI/s3afd3MIj1XXD/aFwSOQjDEK/TZhsdi7V+fXwu8HP48+ Ovh7w3tY2c1wJL5hn5bdPnk57EqCoPqwr9f0RIoUjjRY40UKiKMBQOgA7Cvy zxHzTlp08JF7+8/Tp+N/uP1Xw3yvmqVMZJbe6vV7/hp8x1fiL+1z8Of+Ff8A 7YmtXFpB5WieIh/a1jtHyq0jHz09OJQ5wOiulft1Xx7+2r8OP+Ez/ZQk8S2U Hm6z4TmN8hVcs1q+FuF+gASQn0iNfL8B5v8AUc1gpP3anuv57P7/AMGz6jj7 J/r2UzcV71P3l8t1934pH400UUV/SB/NRPDdXVtZXtrb3VzBaXmz7Zbxyssd zsO5PMUHDFTyCRkHpUPB6cH0pKKVkF29wopc8YPIpKYAQCCCAQRgg969m8Ba tq138N7jRLvVtTutFsb4NZafNdO9vblkydiE4XJ54HHbFeM16v8ADv8A5F3V /wDr8T/0CufFRThdrY6sJKSqaPc9AzRTXdY03SMqr6k/kK9Av/hh400j9nPx H8Utb0ibRvDOk2iXEcV4DHd3wZ0QeXGeVX5wdz4z2B615FfE0qPL7SSXM0lf q3sl3PZo4atW5uSLfKm3bolq2+xwPOKWs/TdV0/V7Iz6ddJcIo/eJjbJH/vK eR9envV/vW7TWjME76o/UT9m7xcnij9mTS7OR1/tDQ2OnTqODsUAxNj02FVz 3KtXvtfmv+yz4y/4R/8AaAfw/cS7NP8AEFv5GCcKLiPLxH8R5ifVxX6UV/Pf GOWfU80qJL3Ze8vnv+Nz+heDsz+u5XTbfvR91/Lb8LBRRRXy59SFFFFABRRR QAV+Vv7fPj9NS+KHhf4c2bo8Wj2xv9QK8kTzDEaH0Kxru9xKPSv1D1TU7LRf DOo6xqU62unWNrJc3UzdI441Lux9gATX883xA8YXvj/42eJ/GWob1uNX1CS5 EbHPlITiOPPoiBVHsor9K8M8q9vmEsTJaU1p/ien5X/A/M/E/NvYZfHCxetR 6/4Vr+dvxOPqzZ/8hqx/6+Y//QxVapYJBDfwTEFhHKrlQcZwQcfpX70z8BTP paX/AI+JiSFVSSzMcADPUnsK4HWfHVjZl4NIVNTuhwZmyIEPt3c/TA964TXf EOq69LIbiZUsN2VtYMrGv+8OrH3PFc7XHSwyteR21cW3pEtXt7d6jqct7fTG 4upMbnIA4HAAA6AdhVWiiutHG3c7fwB/yPM3/Xk/81r2DvXj/gD/AJHqb/ry f+a17BXn4n+IelhP4YdqPzoorA6gooxV/StNvNa8Tado+nxGe/vrlLe3jz95 3YKo/M0pSUU29kOMXJqK3Puj9kbwV9k8J6347u4cT37/AGHT2Yc+ShBkYezO FX6xGvsquf8ACnh2z8JfDbRPDVgB9l060SBWC48wgfM5HqzZY+5NdBX81Z7m Tx+OqV+jenotF+B/SuRZasBgadDqlr6vV/iFVb6ytNS0W806/gjurG6geC4g kGVkjdSrKR6EEirVFeUm07o9ZpNWZ/PN8U/A118Nv2g/Ffgq68xhpl8yW0j9 ZYGw8Mh92jZCfQkiuAr9Lv2+vhxlPC3xTsIOR/xKNXKj/ekt3OP+2qlj/wBM x6V+aNf1LwzmyzHLaVe+trP1Wj/z+Z/KvE+UvLcyq0Oid1/heq+7b1QUUUV7 x4AUUUUAFfSn7PXw18V/E2fXdM8MQ2IW3u4mvry8uAkVqrIcMVB3ueOig+5F fNdd18OdfvvD3xQtLnTtQvNKvJh5cF3aTGKWKQcoQw9TkEHg55BrjzCFaeHm qMkpdG1dfddf132O3LqlGGJg6ybjfVJ2f32f9dtz9oPhn+zv4I+H0ttql3Gf FXimPkalfxjZA3/TCLlY+3zctx96qH7XH/KOb4pf9g6L/wBKYa8Z+G37WV7Z m30j4nWbX1uMIuvafBiRfeeAdf8Aej9PuV6f+05r2i+Jv+CYPxK1nw/qljrG lz6ZEYrm0mEiH/SITjI6EZ5B5HevwSrgM1o55hp468r1I2lvH4lt0Xpp6H9A UsflNbI8TDA2janO8dpfC9+r9bv1PxQgnntb1Lm1nltrlD8ksTbWX8f6V6Ro /j4ErBr0WD0F7AnH/A0H81/KvMu9B6Gv6EnTjPc/nanVlB3R9Q6Tqk2n6xpm t6VcgT280d1Z3EZyNysGRh+IBr9lvCfiG08WfDXQ/Ellj7PqNmk4UHPlsR8y H3VsqfcV+Jehf8iLov8A15J/Kv0U/ZH8Y/bvh9rXgm6lzcaXN9rslY8mCU/O oHosnJ/661+U+ImWe2wUcRFa03r6PT87fifrPhzmnscY8PJ6VFp6rX8r/gfY NFFFfih+2hRRRQAUUUUAfHP7bPxC/wCER/ZNPhmzn8rVvFdz9jUKcMLWPDzs PY/u4z7Smvxtr6r/AGxfiF/wnH7Y+q6daT+bo/hmMaVbBW+UyqSbhsevmEp7 iNa+VK/pXgfKfqOU00170/efz2+5W+Z/MvHWbfX83qNP3Ye6vlv+N/kFFFFf Xnx4qkq4ZSQw6EU5ni2M0pWHAyX6KPc+n1Fdx4A+Gfjj4oeJH03wVoU2prEw F5qEreTY2XvLO3yqf9kZY9hX6I/Cz9lPwL4Ee11jxa0HxC8WR4dGurfbplm/ X91bt/rGB/jlz7IK+Yz7i3L8qTjVlzT/AJVq/n2Xr8kz6jh/hHMc2d6UbQ/m e3y7v0+dj8vLuyvrAWRv7K9sVvIBPZNcW7xi6iPSSMsBvXjquRVav3R8WeFv DXjzwlJoHjPQ7DxJpDcrBdpzAf70LjDRMOxQj8RxX5//ABR/Y88Q6J9o1j4V 3dz4v0kZZtCvGVdTgHUiJ+EuQPT5ZPZq8PIvEbAY2Xs8SvZSe13eL/7e0s/V JefQ93PvDjH4GPtMO/ax62VpL/t3W/ybfkfMvgD/AJHqb/ryf+a17Aa8i8Cx TW3xKvrS5hntLy3tZI7i3njaOWFgVyrowBU+xFeu19liPjPjsIrQDsaKD60d qwOgK+qf2UvBX9t/Ge88WXcW6x0GD9wSOGuZQVX67U3n2JU18rd6/WL4GeCv +EH/AGcNDsJ4fJ1S9X7fqIIwwllAIU+6oEQ+6mvjeOc0+qZbKEX71T3V6dfw 0+Z9nwNlf1vMozkvdp+8/Xp+OvyPX6KKK/BT97CiiigDg/if4Gs/iV8A/FPg i9aONdUsWjgmdciCcYeGTHfbIqNjvjFfhh8Svg/8QPhN4jFh4z0Kezhkcra6 jD+8s7rv+7lAwTjnacMO4Ff0F1maxoukeIfDd1o+u6ZY6xpVymy4tLyBZYpB 6FWBFfY8K8YV8mbhy81OTu1s790/6+R8ZxXwbQzlKfNy1Iqye6a7Nf1bzP5u qK/QP9qT9lbwp8PPhzqHxJ8D6jPpmlRXMcd1oVzmVVMsgQGGQncACfuvnvhu gr8/K/fsmznDZnhlXoPTbXRp9v8Ahj+fs6yXE5XiXh8Qtd9HdNd/w66hRRRX qnkhSqzpIskTFJUYMjDswOQfzpKKAPovTr9NU0Cy1GPgXEQdh/dbow/76BrQ M9z/AMItrehpeX1vo+sweRqtpBOUjukDBhuXpuBAIbGRjrivM/h7qO63v9Hk blD9ptwfQ4Dj88H8TXpFeTVppSaa/rdHtUKjlFST/rZnj2s+CNRsA9xppfVb MclQuJ4x7qPvD3X8q4jOQfbg+1fTIODkZFc7rXhjStbDSTRm0vyOLuAAMf8A fHR/x5966aeJe0jkq4PrEu6F/wAiLov/AF5R/wAq9n+DHjL/AIQf9ovw9rE0 vladLN9j1DJwvky/KzH2U7X/AOACvIdPtmsvD9hZSOsr28CxF1GA2O4Bq53r zcZhoYmjOlPaSa+89TA4mphqsKsN4tP7j9wKK8l+B/jH/hNv2bvD+pTS+bqV rH9hvyTlvNiAXcfdl2P/AMCr1qv5jxmFnhq86M94tr7j+nsHioYmhCtDaST+ 8KKKK5jpCvPPix45g+G37Oni3xpMY/M02wZrRH6SXDYSFD7GRkB9ia9Dr82/ 2/PiF5en+EvhhZT/ADSsdX1VVPO0bo4FPsT5zEH+6hr3eGsq/tHMqVBrRu79 Fq/8jweJs2WXZZVxF9UrL1ei/wAz80rm5nvNRuLu6mkuLqeRpJpXOWdmOSxP ckkmoKKK/qZK2h/KbberCj8jz0PIPsfUe1FFMD9DPg5+1n4Qj8NaX4N8e6Fp Pw+itVEVnqWh2nl6Qe2ZYFy1ux7uNynvivt6Ce3u9Mtr6yuba+sLmPzLW6tZ llhnX+8jqSrD3Br8Fa9M+Gvxf8ffCfVTJ4Q1cDSZJN93oV8pm0+59SY8/u2/ 24yrfWvy3iLw2o4hyrYGXLJ6uL+F+j3T+9eh+qcN+JVbDKNHGx54LRSW6Xps 19z9T9ozXJ+LvG/hbwNpK3XifVFtJJF3W1jCvm3dz/uRDnH+02F96+Urr9qb WvFHw4sZ/Cfh2PwlfzqyXt3c3Au2hkU4YWwwAF7h3BYenevBrq5ur/VrjUL+ 6utQ1C4bdPdXUpkllPqzHk/yFfLZN4d4mpLmxr5Euis5P56pL735Lc+szjxG w1OHLgVzt9Wmkvlo2/uXqeg/E/4gxfEnxxbawfC2j6LJao0UF75YfUp4zgbZ 5xjevAwmCF7GvOPeijvX6zg8HRwlGNGirRjstf1PyXGYytiq0q1V3lLd7fkF FBo4rqOY9a+CHgr/AITr9ozQ9Mni87S7R/t2ogjKmGIg7T7MxRP+BV+s9fKX 7KHgr+xvg9feL7qLbfa5Pttyw5W2iJUY9Nz7z7hVNfVtfg3HOafW8ycIv3af u/Pr+OnyP3rgXK/qmWqpJe9U975dPw1+YUUUV8YfZhRRRQAUUUUAfKX7af8A yYH4j/7CFl/6UJX4qV+1f7af/JgfiP8A7CFl/wClCV+Klfv3hh/yKJf43+UT +ffFL/kbx/wL85BRRRX6Mfm4UUUUAaWj6idI8U2OojJSKT96B/FGeGH5HP4V 9DHGcq25DyrDowPQ/lXzP1HPIr27wbqX9o+BYI5G3XFk32eTPUgcofxXj8K5 MVDRSO3Bz1cTqaDRRXEegA6UUfrQaAPrn9knxl/ZvxQ1fwZdTYttXt/tFmrH pPECSAP9qPcT/wBcxX6EV+LXhnXrzwv8QdF8R2B/0rTryO4QZwH2tkqfYjIP sTX7K6TqdnrfhfTtY0+TzrC+to7i3f8AvI6hlP5EV+LeImWexxkcTFaVFr6r /gW/E/afDrM/bYKWGk9YPT0f/Bv+BoUUUV+eH6IFfhh+1hd3N1+3/wDEP7TP JP5NzBFFuP3EW2iwo9AP6n1r9z6/Cn9qj/k//wCJP/X9D/6TQ1+meFiX9p1f 8D/9KifmHiq2sspf41/6TI+faKKK/eD8DCiiigYUV3Ph7whHrXhCe9nuZrOd 5ito4XcuF4YsvcE8ZB7VzuraFqeiThb+3xCxxHcxndE/0bsfY4NQqkW7X1NH SkoqVtD1XwR/yTS0/wCvib/0KurrlfBH/JNLTr/x8Tf+hV1Wa82p8bPVo/Av QKKKKgsO9b3hjw/eeK/iHo3hzTwfteo3aQIduQgY8ufZRlj7A1g9q+xv2R/B X23xrrPjq7izBpsf2OwYjgzyDMjD3WMgfSWvJzzMlgMDUrvdLT1ei/E9bI8t ePx1Ogtm9fRav8D7q0jSrPQ/Cmm6Lp0fk2Fjapb26eiIoUZ98DrWjRRX81yk 5NtvVn9LRiopJLRBRRRUlBRRRQAUUUUAfKf7aKO37AXiUqjMFvrIsQM7R9oQ ZPoMkD8a/FOv6MvGfhbTvG/wp8Q+EdWXOn6tYSWsrbcmPcpAcf7SnDD3Ar+e XxDoWo+F/Hms+G9Xi8jVNLvZbS6TsJI3Ktj1GRwe4r9x8LMdTlg6uG+1GXN8 mkv0/I/CvFXA1I42lifsyjy/NNv8b/mY9FISFUsegGTV+/02/wBMliS/tpLc SoHhc8pICMgqw4PB6da/Urn5VZlGjtRR2pgFdj4H1H7F42W0dsQX6eScngOO UP55H41x1KrOkiyRMUlRgyMOzA5B/OpnHmi0VCXLJM+lqOtU9Ov01TQLPUY+ BcRB2Ufwt0YfgQaufyryWrHtJpq6DvR2oooGH41+kP7KvjH+3fgRceGrmXdf aBc+WgJ5NvKS8Z/BvMX2AWvzer3P9nfxj/wiP7TOkJPL5em6wP7NusngGQjy 2/CQIM9gWr5ji/LPruWVIpe9H3l8v81dH0/CGZ/Uszpyb92Xuv5/5OzP1Ooo or+ej+hgr8Kf2qP+T/8A4k/9f0P/AKTQ1+4Wta5o/hzw1dazr+qWGjaVbJvn u7ydYo4x7sTj8O9fg58f/FOheNf2wvHPifw1eNqGh314htLkxNH5oWGNCwVg CBuU4yASMHAr9R8LKNT6/Vqcr5eW1+l7rS/c/K/FatT+oUqfMubnvbraz1t2 PHqKKK/cz8JYU5I3lnjhjx5kjhEz0yTgfzptWbL/AJDlj/19R/8AoYoGj6Gt LOPTtJtdPhGIraIRD3x1P4nJ/Gp2VZIHjkRJYnGHjdQysPcHrUsv/H1J/vn+ dR9ulePe57dkirZWNppuni0sYRb2wkZ1jBJCljkgZ7Z7dqtUUAEsAAST0Aob BK2gUfyqkupWL682lx3Mct+sZkeKM7vLAx94jgHnp1q7RYE09hyqzyqkas7s cKqjJJPYV+vfwp8Gr4D+A3h/w6yKt9Hbia/I/iuJPmk574J2g+iivz6/Z08F /wDCYftJ6ZPcReZpWij+0LrI+VmQjyl/GQqcdwrV+pNfkniPmnNUp4OL295+ vT8Lv5o/XPDfK7U6mMkt/dXp1/Gy+TCiiivy4/UgooooAKKKKACiiigAr8kv 27Phz/YHx90r4gWMGzTvEtt5V4VXhbuBQpJ7DfH5ZHqUc1+tteFftIfDj/hZ 37I3ifQ7aDz9as4v7S0gBcsbiEFgi+7oXj/4HX1HB+b/ANnZpTqN2jL3Zej/ AMnZ/I+W4yyf+0cqqU0ryj70fVf5q6+Z+DMn/HvJ/umvo6OGG58NWttdQRXN s9rFvilXcrfIO39etfOMn/HvJ/umvpWwjlntdKtbeGa5upoIkhghjLySsUXh VGST9K/pHFuyTP5rwSu2jznWfAJ+e40GTPc2U78/8Ac/yb8682mjkt7+S0uY 3t7uP/WQyLtdfqP61+lHgf8AZ11XURFqXj65m0GxOGXSLVwb2Uekj8rCPYbn +le8eIPg38LfE/w4h8Kar4L0oaTbg/Y5bRfJvLRj1kjuBmTeep3Fge4NfDY3 xFy/CVlSV6ndxtZfPaXy08+h91gvDjMcXRdXSn2Ur3fy6fPXy6n4uUV9V/FP 9k7xt4KS61nwVJcfELwtGC7xwwhdUs0HUyQLxKo/vxZPHKivlJWVgdpzglWH QqR1BHYj0PNfZ5Zm2EzCl7XDVFJfivVbr5nxOZ5Ti8ureyxNNxf4P0ezXoep fD3Ud1tf6PI3KH7Tbj2PDgfjg/ia9Hr560bUW0nxTY6iM7IpP3o/vRnhx+R/ SvoY4yNrBlIyrDuD0P5U8TC0r9ysJO8LdhO/vR60Ud65zqD605HaOdZEdkdS GVlOCpHQg+tNo7UAfsL8MvFyeOfgX4c8S71a5ubULeAfwzp8kox2G5SR7EV5 J+0T+0ZpnwM0PTLODST4g8WarFJJZWjS+XDAikL5sxHzYJOFVR821uVxmvL/ ANkLxjsvvEfgS6l+WRRqNgpP8Q2pMo+o8sgf7LGvgb9pT4hf8LJ/bC8V6zbz +fo9lN/ZmlMGypggJXcp/uu/mSD/AH6/Isp4Np1eIKtCrG9KHveqfwr+uzP1 zN+M6lHh+lXpStVn7vo18T/rujkviT8XvH/xZ8SjUfGmuz30UblrXT4v3dpa 9v3cQ4BxxuOWPcmvNKKK/b8Ph6VCmqdKKjFbJaI/DMRiateo6lWTlJ7tu7Ci iitjEKs2X/Icsf8Ar5j/APQxVarNl/yHLH/r6j/9DFDBbn0lL/x9Sf75/nUf 50+X/j5k/wB8/wA6ZXjo90zdT1jTNItRLqF0kRI+SJfmlf6KOfxOB715ZrXj XUtSV7exDaVYtwQjZmkH+0/b6L+del6v4f0rW483sBS5C4S7h+WVB6Z/iHsf 0rynWvCWqaMHnCjUNPH/AC8wKcoP9teq/Xke9dWHVPrucWJdXpsaHw+AHjmY AAD7E5/8eWvYO9eP/D8g+OJiCCPsL4IPutfSHgDwpceOPjJ4f8LwbwL66VZ3 XrHCvzSv+CBj9cVhj60KSlUm7JK79Eb5bRnV5acFdt2Xqz9Af2YfBX/CM/s9 x63cw+XqfiGQXbEjDCBcrCv0ILOP+ulfSFQWttb2Wm29naRJb2sESxQxIMKi KMKo9gABU9fy9meOnjcXUry3k7/5L5LQ/qTLMBDBYSnQjtFW+fV/N6hRRRXC d4UUUUAFFFFABRRRQAUUUUAfhP8AtQfDf/hW/wC154n0u1g+z6Jqrf2ppW1c KsM5YsgHYJIJEA9FHrX0L+zp8cPg/pumWOgazpVv8PvGjRJbtr99OZ7bUSAF AFw3Nrn/AJ5sAn+0a91/bm+HP/CTfs42Pjmxg36p4Xuc3BUcvZzFUfp12uI2 9l3n1r8hCAVIIBBGCCODX9BZTTo8S5DCnXm04+62nbVd+jurOzXpZ6n885vU rcNZ/OpQgmn7yur6Pt1VndXT/wAj98dpAQ8FXQPGykFXU9GUjgg9iODTec1+ Pfws+PvxC+FDQ2GlXqa/4TD5k8O6s7PbqO5gcfPbt/ufL6qa+w9T/bR+HkPw wh1PR/DniW/8WzAr/YF2FhitXA+9LdLlXjz08tdzei1+b5p4fZtha6hSh7WL 2a/9uT+H1vbzP0rKvEXKcVQc60vZSS1T1/8AAWt/S1/I+vLi6t9P0u51O9vL XTbC0Xzbi9uZ1hht1H8TyMQFHuT9K/MX9pX4lfBfx3r7f8IP4X/tPxYsg+1+ NbcmyguADynlYzd57SuFx1BavGfiR8W/HvxX1dZvGGsmXTYpC9nolmph0+1/ 3Ys/O3+25Zj6ivNq/QeE+Anl1WOJxFRuoukW0l5N6OXpovJn53xbx9/aVN4a hTSp95JNv06R9dX5oOo55Fe3eDdS/tDwLBG7brmzb7PKSeSByh/FePwrxGux 8Daj9i8araO2IL9PJOTwHHKH88j8a/Q8RDmh6H55hp8tT1PZqKKO1eaeqFFH tRQMv2HivWvBMl54m8PXItNXs7KcQSldwXfG0Z4+jHHocGvlkdBX0Hr3/Ii6 1/15P/KvnwdK6sHTinKaWrsr+S2/N/ecONqTajBvRXdvN2v+S+4KKKK7jgCi ijnsGY9gqkk/gKACrVgM6/p49bqL/wBDFVFZXjV0ZWVhkMDkEVd07/kYtO/6 +4v/AEMUnsNbn0fL/wAfMh/2j/Oo6fJ/x8yH/aNMrx0e4B9e9KCQcg4PsaTt RTEZcOi6bbeJH1W1tltbt42jlEXyxuCQclegbjqK+9/2RPBWItf8fXkXLf8A Eu04sO3DzMP/ACGoI9HFfEtnaXN/q1rY2cL3F5czLDBEg5d2IVVHuSQK/Yvw J4VtvBPwh0DwvbbCLC0VJXXpJKfmkf8A4E5Y/jXwfiDmroYBUE/eqaf9urf9 F95974e5Sq+Pddr3aev/AG89v1f3HW0UUV+Hn7iFFFFABRRRQAUUUUAFFFFA BRRRQBl63o+n+IfBuraBq0AudL1KzktLuI/xxyIUYfkTX89HjzwjqHgL4yeJ fBuqBjeaTfyWxcrjzVB+SQD0dCrj2YV/RXX5cft8fDj7D438NfFCwt9tvqUf 9masyrx58aloXPqWjDr9IRX6V4aZv9Xx8sNJ+7UWn+Jbfer/AIH5n4nZP9Yw EcVFe9Tev+F7/c7fifndRRRX70fgNgooooAKVWdJFkjYpKjBkb0YHIP50lFA H0Xp1+mq6BZ6lHwLiIOw/ut0YfmDVyvN/h7qO62v9HkblD9otx7HhwPxwfxN ekV5VSHLJo9ilPngmHeijtRUGpl67/yImtf9eUn8q+fB0FfQeu/8iNrX/XlJ /KvnwdBXbhPhZ52N+JBRRRXWcYV9p/sNfDr/AISr9qa+8bXsHmaR4Rs90JYA q19cKyRjB/uRea3sWSvip3WOF5HO1EUsx9AOTX7kfsofDhvhx+xf4bt7238j X9bB1nVg33lknAKRnv8AJEIkx2KmvieP82+pZTKMX71T3V6P4vw0+aPuPD7K PrubRnJe7T95+q2/HX5M89+Mv7FfgPx415rvgJ4Ph54ukJkdLeHOmXj8n97A MeWxOPnjx3yrV+Y/jT4WePvhP8TNM0nx54eudIaW+jWzv0Pm2V786/6qcDaT gg7Dhxnla/dXxv8AEXwf8O/D39o+K9ZgsN4P2e1X95c3JH8McQ+Zj74wO5Ff n78Wf2ifEHxH0q98O6bplpoHg2f5ZYLmJLi7u1BBBdiCsXIBAT5h/fr5HgbO c9qWhKPPR7ydrej1b9LP1R9jx1kuRQvOMuSt2jrf1WiXrdejPnqX/j6k/wB4 /wA6Z29aPxor9MPzMPWiiigD6V/Zd8Ff8JJ+0B/b11D5mm+HohckkZU3DZWE fUYdx7xiv0srwz9njwV/wh37NmlNcReXqmsf8TG7yPmUOB5aeoxGF47MWr3O v574wzT69mc2n7sfdXy3+93P6F4Pyv6jlkE170vefz2+5WCiiivlz6gKKKKA CiiigAooooAKKKKACiiigAryz41/D6L4ofsyeLPB5RGvrm0Mumu2Bsuo/nhO ewLKFJ/us3rXqdFb4bETw9aNWm7Si016owxWGp4ijOlUV4yTT9GfzVyxSwXM kM0bwzRsVkjdcMrA4IIPQio6+qP2wfhz/wAIH+2DqmoWcBi0TxMn9q2hA+VZ WJFwmfXzMvjsJFr5Xr+r8sx8MbhKeIhtJJ/8D5PQ/krNMBUwWLqYee8G1/k/ mtQoooruOEKKKKANLRtROk+KbHUeSkUn70f3ozw4/I5/CvoY4zlSGU4Kt/eB 5Br5n6jnkV7b4N1L+0PAsCO265sm+zyknkgDKH8V4/CuTFQ0Ujtwc7PlOqoo oriPQMzXP+RH1r/ryk/lXz2Ogr6E1z/kR9a/68pP5V89joK7cJ8LPOxnxIKK KK6zjPT/AINeENO8bftJeGdJ165tbHwrazf2n4hurqQRxR2VuRJIrMem9tkY 9fMr9IPiT+1nI5n0j4W2iiMZQ+INQg4+sEDdfZpMDn7pr85/AGmiLQbvVJky 91II4cj+BDkn8W7/AOzXoHU18pm+SYbH4uNXELmUFZR6X6t93su2mzPrsnzr FYDBypYd8rm7uXW3RJ9Fv567lzUtS1LWfENxq+s6je6xq1wf315eTGSV/bJ6 D0UYA7CqdHr2or0oxUUklZI89tyd3uw9qO1FB6GmIK9I+EvgxvHnx98P+H3j aSwM/n6gewt4/mcE9t2AgPqwrzev0A/ZI8Ff2f4A1jxzdw7bnVJPslixHIgj b52B9Gk4P/XIV4HE+afUMuqVU/eei9X/AJb/ACPf4Yyv6/mNOk17q1fov89v mfYCqqoFUBVAwABgAUtFFfzkf0cFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF FFFAHyP+2d8OP+E2/ZHutes4PN1rwpKdRhKrlmtiNtyvsNmJD/1xFfi/X9KN 1a299plzZXkMdzaXETRTxSLlZEYEMpHcEEiv59fi54CuPhn+0b4s8FzCQw6f fN9ikfrLbPh4XJ9TGy59Dkdq/bPC7N+ehUwUnrH3l6Pf7nr8z8Q8U8n5K9PG wWkvdfqtvvWnyPOKKKK/WD8kCiiigArsfA2o/YvGq2jtiC/TyTk8CQcofzyP xrjqVWdJFkiYpKjBkb0YHIP51M480WioT5ZJn0tQKp6dfpqnh+z1GPhbiIOw /ut0YfgQa6zwz4U8R+MtfbTfDGlT6rcJjz5AQkFsP70sp+VB9efQGvFrVYUY OdSSilu3ol8z3qFKdaajTTbeyWrfojjdbOPBOs/9eUn8q+eh0FfrN4W/Zz8J 6foNx/wnDjxjfXMDRTWsbPBZQhhg7MEO7Y6OSMHkLXzj8T/2ONc0o3Gr/Ce/ m8UaaMu3h/UJVXUIh1xDKcJcD0Vtrn/arwMv47yepiHQ9pbtJq0X8+nq0l5n u5lwJnFPDqv7O/eKd5L5dfk2+58T1LDBLdXsNrbqWnmkEcYHqTgUt1bXVhrF 1p2oWl3p+o2rmO5tLqFopoGH8LowBU/UV2vgHTvtPiifUnXMVlH+79DK4IH4 hcn8RX3EppQ5j4aFNufKz1W1tYbDS7axgA8m2iEaY74HX8Tk/jU9H40nO8fS vKPYQUveiigYe+KKKKBGnoukXuv+MNM0PTo/Nv7+6jtoF7bnYKM+3PJ7Cv2T 8N6DZeF/AOj+HdOGLPTrRLeIkYLbRgsfcnJPuTXwX+yb4L/tb4saj4xu4t1l osHlWpYcNcSgjI9dse/PoXU1+h9fjPiJmntcXHCxekNX6v8AyX5s/aPDrK/Z YSWKktZ6L0X+b/JBRRRX5yfowUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUU UAFfm/8At9fDnztI8LfFKwgy9uf7J1cqP4GLPA59AG8xST/fQV+kFcR8SPBV l8RfgV4o8FX5RYdWsHhjkcZEMo+aKTH+xIqN/wABr3OG82eXZjSxHROz9Ho/ 8/U8LiXKVmWW1cP1auvVar/L0P53qu6dYy6nrltp8DxRzzsVjaQ4XOCeT26U up6be6P4k1DSNSt3tNRsbmS2uoH+9FIjFXU+4IIrT8J/8lK0b/rv/wCytX9S ua5OZH8rKHv8sjKvtPvdMv8A7LqFtLaz9g44ceqnow+lU6+kLq1tb/T2tL63 hu7dv+Wcq5APqO4PuK801jwBcRu02gu96h5+xyn96P8Acbo/0OD9axp4lPSW htVwso6x1R51RSsrJIyOpV1JDKeoI6ikrpOVnvfwL1f4ax+Ln0f4q6vqmkaC 0olsprcbbcyH7yXEgy8cZwDuUeuSK/WTR7LRtP8ABun2/hqDSrfw4ybrH+y9 ptZBj7ysvDH1JJb1r8IK9U+Gnxm8ffCnU93hfVt+kO4a60W+BmsrjnJzGT8j dfmTBycnNfn/ABjwfXzX95RrNNfZfw+q7Pzd/kfoPBnGVDKX7OtRTT+0viXk +68tPmfsy33OxpSPXkV88/Df9pr4a/EDTlttVvYPAPiVY90tjqtx/o0uBljB cHhuhOxsNgVgeOv2i0iefTPh7aebICVbXNQh+Ue8EB6+zycdwtfj1HhPNqmJ eH9i1Jbt6Jed9n8r36H7LW4uymnhViPbKSeyWsn5W3Xzsu53/wAbfC3wf8Qe CEuvi1DbW90Iium6laNs1gccCAqC0i/7Lho/pX5/6bo+n6FaXFhpU97d2P2q SSG4vIkjuJUJ+UyKhKhgoAwpIra1HUNQ1jXrjVNYv7zVdTnOZru7lMkj+2T0 HsMAdhVTvX7Rw3kdTK8N7KVZzv0+yv8ACunn33sj8W4kzunmmJ9rGioW6/af +J7P7tNrsPek/wCWg+lL3qhqOo2Wk6Y1/qMxtrRCFaTYW+Y/dXjucHGcZr6F anzzdi/39axdT8Q6XpN3FazzGa+kkVFtYcM67iBluyjnvz7V53rXjm/vd9vp avpVoeDJnM8g+vRB7Dn3rj7H/kOWR6k3UZJPJJ3jk+tdUMM95HHUxa2ifSDA rIynkg4yBTakl/4+ZP8AeP8AOvYfgN4K/wCE2/aR0W0uIvN0rT2/tC/BGQUi IKqfUM5RSPQn0ry8Zi4YbDzrT2imz18FhJ4rEQow3k0vvP0H+C3gr/hBP2dt B0iaLytTnj+2aiCMN58oBKn3Vdqf8Ar1Wiiv5lxeJnia86095Nt/M/pvCYWG GoQow2ikl8gooornOgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig D8df23fhx/wiX7UUXi6xg8vSPFdubhiowq3ce1Jx/wACBjkz3Lt6V8peE/8A kpWjf9d//ZWr9pf2rfhz/wALF/Y71+O0g87W9DH9r6dtHzMYlbzUHc7ojIAv dtvpX4g2l3cWOoxXdpKYLqI5jkABKnBGRnvzX9GcB5t9fyiMJP3qfuv06P7t Pkz+buPco+oZxKcV7tT3l6/aX36+jR71q2t6ZokAbULjZKwzHbxjdLJ9F7D3 OBXlOteMdU1ZXt4CdM09uDFE/wC8cf7b9fwGB9a5V3eSd5ZXeWVzl5HYszH1 JPWm19jToRjq9WfHVcTKe2iCiiitznCiiigAIDAhgGB6gius0XxhqmkqkE5O qaeOBDM/zoP9h+o+hyK5QdaSplFSVmVGbi7pn0FpOt6ZrcG7T590yjMltINs qf8AAe49xkVroju+1FLt1OOw9T6V80I7xzpLE7xSoco6MVZT6gjpX0B4fv7u /wDh9pM15N5sskO6VtoBkbcRubHU4A59q4K9Hk1R6WHxHO7M1yAOjBj3K9Pz 719Ffs3WlnqPjLx9p2pWVlqenXOhQx3NneQLNDOv2j7rowIYfX8K+c6+k/2Y /wDkpfjT/sCw/wDpQK+U4vbWTV2uy/8ASon1vCEVLOaCa3b/APSWYvxR/Y40 rURc6z8JL2HQb7ln8N6lOTZSnri3nOWhPokm5P8AaWvhHVPDniDwh8TIPD3i rRdS8O65BcxmSzvoTG5G8fMp6Oh7MpIPrX7h319Y6VodxqeqX1npem24zPd3 cojiT6se/sMk9ga+OvjJ8XPCnjrww3hTS/C+n+ItPik3Qa3rNsQ9s4Od9mvD xngfOxAP9w18twVxVnFaaoVIOrBbyejj6yekvR+8+59Pxvwnk1CDr05qlUe0 VqpeiWsfX4fI+epf+PmT/fP86/Rr9lXwV/YPwQufFF3Ft1DX590RYcrbRkqn 03MXb3BWvgXwh4cvPGPxR0Pw1ZFvtOo3iwmTbny1Jy8h9lUMx9ga/ZDTNOtN I8O2GlafEILGyt0t7eIdERFCqPwAFaeIuaeyw0MJF6z1fov83+RPhzlftcTP FyWkNF6vf7l+Zdooor8cP2QKKKKACiiigAooooAKKKKACiiigAooooAKKKKA CiiigAooooAQgMpDAEEYIPevwP8A2gfh0fhd+1d4q8MwwGHSGn+2aRxhTazZ dFHqEO6PPrGa/fGvgX9vL4cf2z8HtC+JFhb7r7QJ/smpMq8taTMAjE+iS4AH /TZjX3vh3m/1PNFSk/dq+78/s/5fM+A8Rsn+uZW6sV71L3vl9r/P5H5Q0UUV /Q5/OgUUUUAFFdT4P8D+MPiB4wj0DwT4c1PxLqzYLxWsfyQKTgPNIcJEmeNz kD0zX6U/Bz9hbw9ootdd+MF3B4t1cYdNAtGZdNtz1xK3DXDDjrtTI+6w5rwM 74mwGVQvXn73SK1k/l09XZH0GR8MZhmsrUIe71k9Ir59fRXZ+dfhX4Y+OfGX gDxF4u0PQZ28I6HYT3uo63dHybQJChd44nb/AF0uBwiZ5IyRXAA5UH1r98Pj rZ2em/sE/FGx0+0trCxt/CN5HBb28QjjiUQMAqquAAPQV+B6/wCrX6Vw8J8R 1M5p1asoKKjKyW+lur7/ACR3cXcN08mqUaUZuTlG7e2t7aLsLXu/hT/km2i+ 8B/9DavCK938Kf8AJNdG/wCuB/8AQ2r6TFfCj5vB/G/Q6DHNekfDX4ht8N9U 8SalBpK6xf3+nx2tpHLKY4Y2WTeXkI+YjHQLyT3Feb96K8fGYOjiqMqNZXi9 16O/T0PcweLq4WtGtRdpR2fyt1Ol8VeMPE3jbXFv/E+qy6i8Zzb2yr5dtbD0 iiHyr9eWPc1zVH+TUkUUk9zFBBG8s0jBI0RSWZicAAdya0o0adGmoU4qMVsl okZ1q1StUc6knKT3b1bPsv8AZF8FfaPEeu+PLuEGKzT7Bp7MP+WrANKw9Cqb V+kjV95Vwnwz8Hx+BPgf4e8NBUFzbWwa8Zed87/PIc9xuJA9gK7uv514kzT6 /mFSsn7uy9Ft9+/zP6L4byv+z8up0Wvetd+r3+7b5BRRRXhHuhRRRQAUUUUA FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUNzbW95p89peW8N1azIUmh mQOkikYKsp4II7GpqKabTuDVz4O+Mf7EPhbxOLrXPhdPb+D9cbLtpU2Tp9wf RMZaAk+m5ewVetfmX44+HvjL4ceMX0LxpoN9od+MmLzlzHOoON8cgysi+6k1 /RLXNeK/B3hfxz4Rn0Hxdoen6/pMv3oLuPdtP95W6ow7MpBHrX6Hw/4h43BW p4n95D/yZfPr8/vR+dcQ+HWCxt6mG/dz/wDJX6rp6r7mfzmUV+hnxi/YZ1fS vteu/CO8k13Txl30G+kAuohySIpDhZR6K2G93NfAGo6bqOj65daZq1jeaZqV tIY7i1uoWiliYdVZWAIPsa/acoz3BZnT58NO/ddV6r+kfiWb5DjssqcmJhbs +j9H/TNTwv4u8VeCfFsGveD/ABDqvhvV4iMXFjMV3gHO2RD8siequCPav0g+ Dv7dul34tdC+M9jDoF7gIviXTomNlKcYzPFy0BPdl3Jk/wAAr8vqKxzrhvAZ pC2Ihr0ktJL5/o7ryNsk4lx+VTvh56dYvWL+X6qz8z96/jjqWnax+wF8UNT0 m/stU0258JXr293aTrLDMphb5ldSQw9wa/BJf9Wv0r0PwL478YeGrLU/B2ia /eWfhXxLBJp2saS4EttLHMux3SNsiOYA8SJg565rI1nwfqmjo80QOpaev/Le FTvQf7adR9RkV5vCvD7yWFWjKfMpO6ezta2vn8z0uLOIVnc6VeMHFxVmt1e9 9PL5HK17v4T/AOSa6N/1wP8A6G1eDgggEEEHoQa948KD/i2ujf8AXA/+htX0 uL+FHzWDfvv0Ogorq/CXgjxR468RDTPDGkXWpzjHmug2xQg/xO5+VR9Tz2zX 3V8Nv2XPDnh0W+qeN5IfFGsDDCzAP2KE+hB5l/4Fhf8AZ718lnPEmBy2P72V 5fyrf/gfM+xybhvHZlL91G0f5nov+D8j5C+HfwX8b/Ei5jm0ux/s/RN2JNVv QUgHrs7yH2Xj1Ir7++HHwI8EfDsQ3sVt/bviJME6pfICyN6xJyI/qMt/tGva Ioo4LaOGGNIYY1CpGihVUAYAAHQCn1+P55xjjsxvBPkh2XX1fX8F5H7DkfBu By602uefd9PRdPxfmFFFFfJH1oUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUU UUAFFFFABRRRQAUUUUAFFFFABRRRQAV5L8Ufgj8O/i7obW/i7RI21JY9ttq9 piK9t/TbJj5h/suGX2r1qit8Niq2HqKpSk4yXVaHPisLRxNJ060VKL6PVH4s fGP9kX4ifDIXWsaJHJ448IxksbywhP2m2X/ptCMnAHV13L3O3pXydX9LFfK/ xj/ZL+HXxSF1q2mQp4K8Xvlv7R0+EeTct/03h4Dc9WXa3qT0r9a4f8TNqWYL /t9fqv1X3H5HxD4Yb1cvf/bj/R/o/vPxl0H/AJHvRf8Ar9j/AJ19B5YPlSQc 8EVzvjX4FfEb4QfFTR4vFWjPJpLagi22s2OZbOf5uPnx8jH+64VvYjmvrD4b /s1+MPGbQalr6yeE/D74YSXEf+lTr/sRHoCP4mx6gNX3+YZ5gKdCOJlVXI1o 73v6d35HwGW5FmFSvLDKk+dbq1reb7LzPlG4+Hsfi3W4rXRLG5TX7l9sSWMB k89vRo16/VcGvtv4M/si6jB4O0ib4o3S2fkR86Pp8253+Yn95KPujn7q5P8A tA19keB/hp4O+Hmj/ZfDOkx287qFnvpv3lzP/vyHnH+yML6Cu8r8oz3xDxOI TpYT3I938Xy7fi/Q/Wci8OsNh5Kti/fl2Xw/Pv8AgvJmPoegaL4Z8Ow6ToGm Wek6dF9yC2jCrnuT3LHuTknvWxRRX5xOcpycpO7Z+kQhGEVGKskFFFFSUFFF FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU AFFFFABRRRQAUUUUAYHib/kUZP8Ar5t//R8db9FFbS/hL1f6GS/iv0X6hRRR WJqFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//2Q== name vagrant passwd ******** passwordpolicyoptions PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+ZmFp bGVkTG9naW5Db3VudDwva2V5PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxr ZXk+ZmFpbGVkTG9naW5UaW1lc3RhbXA8L2tleT4KCTxkYXRlPjIwMDEtMDEt MDFUMDA6MDA6MDBaPC9kYXRlPgoJPGtleT5sYXN0TG9naW5UaW1lc3RhbXA8 L2tleT4KCTxkYXRlPjIwMDEtMDEtMDFUMDA6MDA6MDBaPC9kYXRlPgo8L2Rp Y3Q+CjwvcGxpc3Q+Cg== realname vagrant shell /bin/bash uid 501 chef-12.3.0/spec/data/file-providers-method-snapshot-chef-11-4.json0000644000004100000410000000560112520074675024632 0ustar www-datawww-data{ "Chef::Provider::CookbookFile": [ "action_create", "file_cache_location", "resource_cookbook", "backup_new_resource", "content_stale?", "diff_current_from_content", "is_binary?", "diff_current", "setup_acl", "compare_content", "set_content", "update_new_file_state", "set_all_access_controls", "action_create_if_missing", "action_delete", "action_touch", "backup", "deploy_tempfile", "shell_out", "shell_out!", "run_command_compatible_options", "checksum", "access_controls", "enforce_ownership_and_permissions" ], "Chef::Provider::RemoteFile": [ "action_create", "current_resource_matches_target_checksum?", "matches_current_checksum?", "backup_new_resource", "source_file", "http_client_opts", "diff_current_from_content", "is_binary?", "diff_current", "setup_acl", "compare_content", "set_content", "update_new_file_state", "set_all_access_controls", "action_create_if_missing", "action_delete", "action_touch", "backup", "deploy_tempfile", "shell_out", "shell_out!", "run_command_compatible_options", "checksum", "access_controls", "enforce_ownership_and_permissions" ], "Chef::Provider::Template": [ "action_create", "template_finder", "template_location", "resource_cookbook", "rendered", "content_matches?", "render_template", "diff_current_from_content", "is_binary?", "diff_current", "setup_acl", "compare_content", "set_content", "update_new_file_state", "set_all_access_controls", "action_create_if_missing", "action_delete", "action_touch", "backup", "deploy_tempfile", "shell_out", "shell_out!", "run_command_compatible_options", "checksum", "access_controls", "enforce_ownership_and_permissions" ], "Chef::Provider::Directory": [ "action_create", "action_delete", "diff_current_from_content", "is_binary?", "diff_current", "setup_acl", "compare_content", "set_content", "update_new_file_state", "set_all_access_controls", "action_create_if_missing", "action_touch", "backup", "deploy_tempfile", "shell_out", "shell_out!", "run_command_compatible_options", "checksum", "access_controls", "enforce_ownership_and_permissions" ], "Chef::Provider::File": [ "diff_current_from_content", "is_binary?", "diff_current", "setup_acl", "compare_content", "set_content", "update_new_file_state", "action_create", "set_all_access_controls", "action_create_if_missing", "action_delete", "action_touch", "backup", "deploy_tempfile", "shell_out", "shell_out!", "run_command_compatible_options", "checksum", "access_controls", "enforce_ownership_and_permissions" ] } chef-12.3.0/spec/data/recipes.tgz0000644000004100000410000000044512520074675016570 0ustar www-datawww-dataTj0`*oOzbR?[/16ڭlxL-VGDF픜z"ke 3 )-BK+Ji|wu!4W3X̵T]toVHU1J+gr0Ͽ }P/>,|ϟMJ A p?Ͽm "a big monster" do cat "#{params[:name]}" do pretty_kitty true end end chef-12.3.0/spec/data/cookbooks/openldap/definitions/server.rb0000644000004100000410000000016412520074675024347 0ustar www-datawww-datadefine :openldap_server, :mothra => "a big monster" do cat "#{params[:name]}" do pretty_kitty true end end chef-12.3.0/spec/data/cookbooks/openldap/files/0000755000004100000410000000000012520074675021302 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/files/default/0000755000004100000410000000000012520074675022726 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/0000755000004100000410000000000012520074675024720 5ustar www-datawww-data././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/chef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdi0000755000004100000410000000000012520074675034124 5ustar www-datawww-data././@LongLink0000000000000000000000000000016500000000000011567 Lustar rootrootchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/chef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdi0000755000004100000410000000000012520074675034124 5ustar www-datawww-data././@LongLink0000000000000000000000000000020200000000000011557 Lustar rootrootchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdi0000644000004100000410000000020512520074675034123 0ustar www-datawww-data# remote directory # file specificity: default # relpath: remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txt chef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/0000755000004100000410000000000012520074675026563 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir0000644000004100000410000000003612520074675033044 0ustar www-datawww-datathis is a dotfile in a dotdir chef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file2.txt0000644000004100000410000000013012520074675030665 0ustar www-datawww-data# remote directory # file specificity: default # relpath: remotedir/remote_dir_file2.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file1.txt0000644000004100000410000000013012520074675030664 0ustar www-datawww-data# remote directory # file specificity: default # relpath: remotedir/remote_dir_file1.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb0000644000004100000410000000016112520074675030403 0ustar www-datawww-data# This file is not a chef template despite being and erb. # It should not be included in a cookbook syntax check chef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/0000755000004100000410000000000012520074675027424 5ustar www-datawww-data././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.tx0000644000004100000410000000014512520074675033724 0ustar www-datawww-data# remote directory # file specificity: default # relpath: remotedir/remotesubdir/remote_dir_file1.txt././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.tx0000644000004100000410000000014512520074675033725 0ustar www-datawww-data# remote directory # file specificity: default # relpath: remotedir/remotesubdir/remote_dir_file2.txtchef-12.3.0/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile0000644000004100000410000000006212520074675031351 0ustar www-datawww-datathis is a file with a name beginning with a . dot chef-12.3.0/spec/data/cookbooks/openldap/files/default/.ssh/0000755000004100000410000000000012520074675023601 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/files/default/.ssh/id_rsa0000644000004100000410000000001012520074675024754 0ustar www-datawww-dataFAKE KEYchef-12.3.0/spec/data/cookbooks/openldap/files/default/.dotfile0000644000004100000410000000006612520074675024357 0ustar www-datawww-dataI am here to test .dotfiles work in file directories. chef-12.3.0/spec/data/cookbooks/openldap/templates/0000755000004100000410000000000012520074675022176 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/templates/default/0000755000004100000410000000000012520074675023622 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb0000644000004100000410000000004712520074675031371 0ustar www-datawww-data<%= render("helper_test.erb").strip %> chef-12.3.0/spec/data/cookbooks/openldap/templates/default/openldap_variable_stuff.conf.erb0000644000004100000410000000004012520074675032110 0ustar www-datawww-datasuper secret is <%= @secret -%> chef-12.3.0/spec/data/cookbooks/openldap/templates/default/helper_test.erb0000644000004100000410000000002512520074675026627 0ustar www-datawww-data<%= helper_method %> chef-12.3.0/spec/data/cookbooks/openldap/templates/default/openldap_stuff.conf.erb0000644000004100000410000000004712520074675030252 0ustar www-datawww-dataslappiness is <%= node[:slappiness] -%>chef-12.3.0/spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb0000644000004100000410000000010412520074675031213 0ustar www-datawww-dataTemplate rendering libraries should support different line endings chef-12.3.0/spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb0000644000004100000410000000010612520074675031544 0ustar www-datawww-dataTemplate rendering libraries should support different line endings chef-12.3.0/spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb0000644000004100000410000000011012520074675031344 0ustar www-datawww-dataTemplate rendering libraries should support different line endings chef-12.3.0/spec/data/cookbooks/openldap/templates/default/test.erb0000644000004100000410000000003712520074675025273 0ustar www-datawww-dataWe could be diving for pearls! chef-12.3.0/spec/data/cookbooks/openldap/metadata.rb0000644000004100000410000000065412520074675022312 0ustar www-datawww-dataname "openldap" maintainer "Opscode, Inc." maintainer_email "cookbooks@opscode.com" license "Apache 2.0" description "Installs and configures all aspects of openldap using Debian style symlinks with helper definitions" long_description "The long description for the openldap cookbook from metadata.rb" version "8.9.10" recipe "openldap", "Main Open LDAP configuration" chef-12.3.0/spec/data/cookbooks/openldap/attributes/0000755000004100000410000000000012520074675022366 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/attributes/default.rb0000644000004100000410000000073512520074675024344 0ustar www-datawww-datachef_env ||= nil case chef_env when "prod" default[:ldap_server] = "ops1prod" default[:ldap_basedn] = "dc=hjksolutions,dc=com" default[:ldap_replication_password] = "yes" when "corp" default[:ldap_server] = "ops1prod" default[:ldap_basedn] = "dc=hjksolutions,dc=com" default[:ldap_replication_password] = "yougotit" else default[:ldap_server] = "ops1prod" default[:ldap_basedn] = "dc=hjksolutions,dc=com" default[:ldap_replication_password] = "forsure" end chef-12.3.0/spec/data/cookbooks/openldap/attributes/smokey.rb0000644000004100000410000000003612520074675024221 0ustar www-datawww-datadefault[:smokey] = "robinson" chef-12.3.0/spec/data/cookbooks/openldap/libraries/0000755000004100000410000000000012520074675022154 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/libraries/openldap.rb0000644000004100000410000000007512520074675024305 0ustar www-datawww-datarequire_relative './openldap/version.rb' class OpenLDAP end chef-12.3.0/spec/data/cookbooks/openldap/libraries/openldap/0000755000004100000410000000000012520074675023756 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/libraries/openldap/version.rb0000644000004100000410000000005012520074675025763 0ustar www-datawww-dataclass OpenLDAP VERSION = '8.9.10' end chef-12.3.0/spec/data/cookbooks/openldap/recipes/0000755000004100000410000000000012520074675021632 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/openldap/recipes/default.rb0000644000004100000410000000005212520074675023600 0ustar www-datawww-data cat "blanket" do pretty_kitty true end chef-12.3.0/spec/data/cookbooks/openldap/recipes/return.rb0000644000004100000410000000005012520074675023471 0ustar www-datawww-data# CHEF-5199 regression test. return nil chef-12.3.0/spec/data/cookbooks/openldap/recipes/one.rb0000644000004100000410000000035212520074675022740 0ustar www-datawww-data## # Nodes should have a unique name ## name "test.example.com-default" ## # Nodes can set arbitrary arguments ## sunshine "in" something "else" ## # Nodes should have recipes ## recipes "operations-master", "operations-monitoring" chef-12.3.0/spec/data/cookbooks/openldap/recipes/gigantor.rb0000644000004100000410000000005212520074675023766 0ustar www-datawww-datacat "blanket" do pretty_kitty false end chef-12.3.0/spec/data/cookbooks/java/0000755000004100000410000000000012520074675017317 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/java/files/0000755000004100000410000000000012520074675020421 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/java/files/default/0000755000004100000410000000000012520074675022045 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/java/files/default/java.response0000644000004100000410000000013412520074675024544 0ustar www-datawww-data# Hi, I'm pretending to be the preseed file for installing the Sun JDK on debian # or Ubuntuchef-12.3.0/spec/data/cookbooks/java/metadata.rb0000644000004100000410000000003412520074675021421 0ustar www-datawww-dataname "java" version "0.0.1" chef-12.3.0/spec/data/cookbooks/borken/0000755000004100000410000000000012520074675017656 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/borken/templates/0000755000004100000410000000000012520074675021654 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/borken/templates/default/0000755000004100000410000000000012520074675023300 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/borken/templates/default/borken.erb0000644000004100000410000000012312520074675025246 0ustar www-datawww-dataa cat walked on the keyboard one day... <%= (*&)(*^^^^*******++_+_--- }}}}]]]end)%>chef-12.3.0/spec/data/cookbooks/borken/metadata.rb0000644000004100000410000000003612520074675021762 0ustar www-datawww-dataname "borken" version "1.0.0" chef-12.3.0/spec/data/cookbooks/borken/recipes/0000755000004100000410000000000012520074675021310 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/borken/recipes/default.rb0000644000004100000410000000013412520074675023257 0ustar www-datawww-dataa cat walked on the keyboard one day... (*&(*&(*&(*&(*^%$%^%#^^&(*)(*{}}}}}}}}+++++===))))))chef-12.3.0/spec/data/cookbooks/chefignore0000644000004100000410000000026112520074675020431 0ustar www-datawww-data# # The ignore file allows you to skip files in cookbooks with the same name that appear # later in the search path. # recipes/ignoreme.rb # comments can be indented ignored chef-12.3.0/spec/data/cookbooks/ignorken/0000755000004100000410000000000012520074675020212 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/files/0000755000004100000410000000000012520074675021314 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/files/default/0000755000004100000410000000000012520074675022740 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/files/default/not_me.rb0000644000004100000410000000013512520074675024545 0ustar www-datawww-dataa cat walked on the keyboard one day... (*&(*&(*&(*&(*^%$%^%#^^&(*)(*{}}}}}}}}+++++===)))))) chef-12.3.0/spec/data/cookbooks/ignorken/templates/0000755000004100000410000000000012520074675022210 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/templates/ubuntu-12.10/0000755000004100000410000000000012520074675024171 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/templates/ubuntu-12.10/not_me.rb0000644000004100000410000000013512520074675025776 0ustar www-datawww-dataa cat walked on the keyboard one day... (*&(*&(*&(*&(*^%$%^%#^^&(*)(*{}}}}}}}}+++++===)))))) chef-12.3.0/spec/data/cookbooks/ignorken/metadata.rb0000644000004100000410000000004012520074675022311 0ustar www-datawww-dataname "ignorken" version "1.0.0" chef-12.3.0/spec/data/cookbooks/ignorken/recipes/0000755000004100000410000000000012520074675021644 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/ignorken/recipes/default.rb0000644000004100000410000000001712520074675023613 0ustar www-datawww-data# This is fine!chef-12.3.0/spec/data/cookbooks/ignorken/recipes/ignoreme.rb0000644000004100000410000000013412520074675023774 0ustar www-datawww-dataa cat walked on the keyboard one day... (*&(*&(*&(*&(*^%$%^%#^^&(*)(*{}}}}}}}}+++++===))))))chef-12.3.0/spec/data/cookbooks/preseed/0000755000004100000410000000000012520074675020025 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/preseed/files/0000755000004100000410000000000012520074675021127 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/preseed/files/default/0000755000004100000410000000000012520074675022553 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/preseed/files/default/preseed-template.seed0000644000004100000410000000042112520074675026652 0ustar www-datawww-data# This file should never be executed by the preseeding tests # This is here to verify that templates are preferred over cookbook_files when # preseeding packages. chef-integration-test chef-integration-test/sample-var string "WRONG-cookbook file used instead of template!" chef-12.3.0/spec/data/cookbooks/preseed/files/default/preseed-file.seed0000644000004100000410000000011412520074675025755 0ustar www-datawww-datachef-integration-test chef-integration-test/sample-var string "hello world" chef-12.3.0/spec/data/cookbooks/preseed/templates/0000755000004100000410000000000012520074675022023 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/preseed/templates/default/0000755000004100000410000000000012520074675023447 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/preseed/templates/default/preseed-template.seed0000644000004100000410000000013512520074675027550 0ustar www-datawww-datachef-integration-test chef-integration-test/sample-var string "<%= node[:preseed_value] -%>" chef-12.3.0/spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed0000644000004100000410000000013312520074675031514 0ustar www-datawww-datachef-integration-test chef-integration-test/sample-var string "<%= @template_variable -%>" chef-12.3.0/spec/data/cookbooks/preseed/metadata.rb0000644000004100000410000000003712520074675022132 0ustar www-datawww-dataname "preseed" version "1.0.0" chef-12.3.0/spec/data/cookbooks/name-mismatch-versionnumber/0000755000004100000410000000000012520074675024015 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb0000644000004100000410000000034112520074675026120 0ustar www-datawww-dataname 'name-mismatch' maintainer '' maintainer_email '' license '' description 'Installs/Configures name-mismatch' long_description 'Installs/Configures name-mismatch' version '0.1.0' chef-12.3.0/spec/data/cookbooks/name-mismatch-versionnumber/README.md0000644000004100000410000000007512520074675025276 0ustar www-datawww-data# name-mismatch TODO: Enter the cookbook description here. chef-12.3.0/spec/data/cookbooks/name-mismatch-versionnumber/recipes/0000755000004100000410000000000012520074675025447 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb0000644000004100000410000000012412520074675027415 0ustar www-datawww-data# # Cookbook Name:: name-mismatch # Recipe:: default # # Copyright (C) 2014 # # # chef-12.3.0/spec/data/cookbooks/apache2/0000755000004100000410000000000012520074675017701 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/apache2/files/0000755000004100000410000000000012520074675021003 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/apache2/files/default/0000755000004100000410000000000012520074675022427 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/apache2/files/default/apache2_module_conf_generate.pl0000644000004100000410000000010012520074675030502 0ustar www-datawww-data# apache2_module_conf_generate.pl # this is just here for show. chef-12.3.0/spec/data/cookbooks/apache2/metadata.rb0000644000004100000410000000003712520074675022006 0ustar www-datawww-dataname "apache2" version "0.0.1" chef-12.3.0/spec/data/cookbooks/apache2/recipes/0000755000004100000410000000000012520074675021333 5ustar www-datawww-datachef-12.3.0/spec/data/cookbooks/apache2/recipes/default.rb0000644000004100000410000000003212520074675023277 0ustar www-datawww-data# # Nothing ot see here # chef-12.3.0/spec/data/lwrp_const_scoping/0000755000004100000410000000000012520074675020321 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp_const_scoping/resources/0000755000004100000410000000000012520074675022333 5ustar www-datawww-datachef-12.3.0/spec/data/lwrp_const_scoping/resources/conflict.rb0000644000004100000410000000000012520074675024447 0ustar www-datawww-datachef-12.3.0/spec/data/git_bundles/0000755000004100000410000000000012520074675016704 5ustar www-datawww-datachef-12.3.0/spec/data/git_bundles/sinatra-test-app-with-symlinks.gitbundle0000644000004100000410000000443212520074675026622 0ustar www-datawww-data# v2 git bundle 5a4748c52aaea8250b4346a9b8ede95ee3755e28 refs/heads/master PACKxM @= h$x) )+|_`j-tuTcx]0'5&gfOX \mBLѦiSQGYMm 'G3;\ܒV=**wo5A:̭SP?CIxAn hW1GJpD|73,%LݓEG"'G:(`n'ܹQ-XOi 1Hv=ի|ϗ`M·~~HY>Iop[t1ÇM֚E64 ,vϦKa*hK\ZQ1xA @=:2' U\[AjCn?, H/$9hP]d"YWR*&wS(qY1qAN!VB$P}@sVyJ~5KS~!a 7DW3Ύ6*|dO vv iCLx !bаH%'@Zs&3k0ÆDKAv} DNڜAt\(->:1>i}d3 Ze~ zI'X?j/y ԻyIG xQ =^EcJU>dn_'4"l(ٮ x340031QpOMIe~'y06ww7DQz` ٽZ%U WG_W[>r'˞Sߺ($0P(<۝ϋӸKwKٜ̼bl5w*sWF>xKOUP*K,)JT$xswRP(.HM.24 L 5E%%y 9#=D,K,)JI#K+h)EulA,`F0̜ P!rP7 `\QiR%k+H:.zQ:x=AK1zz+Rċ9Κ$M&{'X0住fap|} ;<(R( VM *Tb͎8OSgG54+d ;dsi{jYQ̹rVS9, )O\88ʾJÑ I۾ׯťZ?t^Fx340031QH,(+Jb,ZqdH{++o|M Wx1 НS8L&be4ysG]PBgM_fp |rρG<&[bGx342 WG_W̼l+y/fYؽe{=ղ`޷exccP%3d9.hȷ.M x340031QH,(+Jb]QbߺFm7e[ǯ x10НStLEkLB[CRWFtJ7p&>5)#?k1 zF3xccP%2}.ilPXl= Qx340031QH,(+Jb˪wꕭ?5]gtWZK x10НStL$V@ϯ1y n EKsFtjF_pMK|j*EXOٛI Kl ΛĚchef-12.3.0/spec/data/git_bundles/sinatra-test-app-with-callback-files.gitbundle0000644000004100000410000000670712520074675027614 0ustar www-datawww-data# v2 git bundle 2404d015882659754bdb93ad6e4b4d3d02691a82 refs/heads/with-deploy-scripts PACK&x[jD!D]Eo`B;>Z! J_mɝQ"$XDw|6"Ps0%:dYb)8&6#G.Iz*rJM_Բ|z6/8-ܐ^_u-Jۄzzʮې yaQMxMn! @=IUիY|Tb`Dkj|fFPY둑ǜ\ɺo4)שm6>w;'8.%tFkKim :Z?'+pi{?NxxAn hW1GJpD|73,%LݓEG"'G:(`n'ܹQ-XOi 1Hv=ի|ϗ`M·~~HY>Iop[t1ÇM֚E64 ,vϦKa*hK\ZQ1xA @=:2' U\[AjCn?, H/$9hP]d"YWR*&wS(qY1qAN!VB$P}@sVyJ~5KS~!a 7DW3Ύ6*|dO vv iCLx !bаH%'@Zs&3k0ÆDKAv} DNڜAt\(->:1>i}d3 Ze~ zI'X?j/y ԻyIG xQ =^EcJU>dn_'4"l(ٯ x340031QpOMIe~'y06ww7DQz` ٽZ%U WG_W[>r'˞Sߺ($0m1Y:A?' wȦW2zxnyڎW"VG>xKOUP*K,)JT$xswRP(.HM.24 L 5E%%y 9#=D,K,)JI#K+h)EulA,`F0̜ P!rP7 `\QiR%k+H:.zQ:x=AK1zz+Rċ9Κ$M&{'X0住fap|} ;<(R( VM *Tb͎8OSgG54+d ;dsi{jYQ̹rVS9, )O\88ʾJÑ I۾ׯťZ?t^Fx340031QH,(+JbXEh 3?L xK 0 :Ûl=jY5Yy0.[]MSPU'QSQjFcWڝkf' 0?I/' x340031QHL+I-/J-.I,*+Jb2UM:ƻbMSm^.IiEE% I 525Nx ]f0ob$ ʱ/@P65+ss2AĒ?M=਴sF;Cx A нt2#Dw~AUdEVСy([,ſHZ ˆI5FxSV((OQ(/IQHJU(HU(JIM,N/H,JIUPRv,*OJM/JL/J,I+JRg?TjOxssZRQox A нtP뭟yYmHAi!f!=ۮo1CFXLJϛ֘|xSV((OQ(/IQHJU(HU(JIM,N/H,JIUPRv,*OJM/J/+(Qwx[ϸqB糞Emڹ>Gs f PHI-ɯdxD6 {&Nj=~;-Fzx340031QH,(+JbP \ZؿwAskIx+.˜6f x340031QHL+I-/J-.I,*+Jb\eڰO7r_\bQ_^X R,Skջ&OATH&;l޽/gausȅ7QWde?Ur2=s! rv|ƔF ixxR jdxccP%3d9.hȷ.M x340031QH,(+Jb]QbߺFm7e[ǯ x10НStLEkLB[CRWFtJ7p&>5)#?k1 zF3xccP%2}.ilPXl= Qx340031QH,(+Jb˪wꕭ?5]gtWZK x10НStL$V@ϯ1y n EKsFtjF_pMK|j*EXOٛI K^-;dd*6pchef-12.3.0/spec/data/git_bundles/example-repo.gitbundle0000644000004100000410000000227612520074675023210 0ustar www-datawww-data# v2 git bundle bc5ec79931ae74089aeadca6edc173527613e6d9 refs/heads/foo123 d294fbfd05aa7709ad9a9b8ef6343b17d355bf5f refs/heads/master 9b73fb5e316bfaff7b822b0ccb3e1e08f9885085 refs/tags/v1.0.0 d294fbfd05aa7709ad9a9b8ef6343b17d355bf5f HEAD PACK x 0 $MS !^,vHF!OWyUlea_ŷLBDkM\Ž>@+eib^gbkwc;ppXs Ry3;C?f]Asf~CQ x 0 @{ n"!ĉI\6U+p|FW" 1*vvb]1jS`9U"Km@\ϥhqifj"!|:t,Oi܎3m?Jz-mEͩ{ 4sk [,^=@& x 0 @{ 'O#!ĉ q\Z6Uk+p|t*ql@nJ:b4U*TLH8"q& .EK[6S%:|p;x}=f`B4.C cB{ x 0 @{H0HUSH+p/+}W)"Tr'Hc*URwxHQ(/IQ$}2űթt`$chef-12.3.0/spec/data/git_bundles/sinatra-test-app.gitbundle0000644000004100000410000000400512520074675023776 0ustar www-datawww-data# v2 git bundle 3eb5ca6c353c83d9179dd3b29347539829b401f3 refs/heads/master PACKxAn hW1GJpD|73,%LݓEG"'G:(`n'ܹQ-XOi 1Hv=ի|ϗ`M·~~HY>Iop[t1ÇM֚E64 ,vϦKa*hK\ZQ1xA @=:2' U\[AjCn?, H/$9hP]d"YWR*&wS(qY1qAN!VB$P}@sVyJ~5KS~!a 7DW3Ύ6*|dO vv iCLx !bаH%'@Zs&3k0ÆDKAv} DNڜAt\(->:1>i}d3 Ze~ zI'X?j/y ԻyIG xQ =^EcJU>dn_'4"l(ٮx340031QpOMIe~'y06ww7DQz` ٽZ%U WG_W[>r'˞Sߺ($0P(<۝ϋӸKwK8>xKOUP*K,)JT$xswRP(.HM.24 L 5E%%y 9#=D,K,)JI#K+h)EulA,`F0̜ P!rP7 `\QiR%k+H:.zQ:x=AK1zz+Rċ9Κ$M&{'X0住fap|} ;<(R( VM *Tb͎8OSgG54+d ;dsi{jYQ̹rVS9, )O\88ʾJÑ I۾ׯťZ?t^Fx340031QH,(+Jb,ZqdH{++o|M Wx1 НS8L&be4ysG]PBgM_fp |rρG<&[bGx340031QpOMIe~'y06ww7DQz` ٽZ%U WG_W;[Oسm[͓&@XP`g%~ҕi+?V06:f*x[qw ,x340031QH,(+JbHa:Hݏ{mŘ x10FSaHV@ocޏTH#O%v5tx5Ҧc;}>xccP%3d9.hȷ.M x340031QH,(+Jb]QbߺFm7e[ǯ x10НStLEkLB[CRWFtJ7p&>5)#?k1 zF3xccP%2}.ilPXl= Qx340031QH,(+Jb˪wꕭ?5]gtWZK x10НStL$V@ϯ1y n EKsFtjF_pMK|j*EXOٛI KTND;N#Y븬chef-12.3.0/spec/data/knife-site-subcommands/0000755000004100000410000000000012520074675020754 5ustar www-datawww-datachef-12.3.0/spec/data/knife-site-subcommands/plugins/0000755000004100000410000000000012520074675022435 5ustar www-datawww-datachef-12.3.0/spec/data/knife-site-subcommands/plugins/knife/0000755000004100000410000000000012520074675023531 5ustar www-datawww-datachef-12.3.0/spec/data/knife-site-subcommands/plugins/knife/example_subcommand.rb0000644000004100000410000000000012520074675027707 0ustar www-datawww-datachef-12.3.0/spec/data/knife-home/0000755000004100000410000000000012520074675016427 5ustar www-datawww-datachef-12.3.0/spec/data/knife-home/.chef/0000755000004100000410000000000012520074675017412 5ustar www-datawww-datachef-12.3.0/spec/data/knife-home/.chef/plugins/0000755000004100000410000000000012520074675021073 5ustar www-datawww-datachef-12.3.0/spec/data/knife-home/.chef/plugins/knife/0000755000004100000410000000000012520074675022167 5ustar www-datawww-datachef-12.3.0/spec/data/knife-home/.chef/plugins/knife/example_home_subcommand.rb0000644000004100000410000000000012520074675027355 0ustar www-datawww-datachef-12.3.0/spec/data/checksum/0000755000004100000410000000000012520074675016207 5ustar www-datawww-datachef-12.3.0/spec/data/checksum/random.txt0000644000004100000410000000200012520074675020220 0ustar www-datawww-datae4010abcac515ef3b78e92ee1f848a0d3bc3a526fcb826e7b4d39a6d516aa0487085c9b1be35e8d909617b250dca36dd4a55f01b7cdd310826bfd748cb27e0e43dd52b22968383c8086b06ee2d16e13574f98c058ce2bc3475b92ecf9c16e504022d60b132643986a8e7908d067526e20b4bafe1eb75349f27a4d3de02b077e76a2f59b73c14413f11e7208ae0bf6a408d51a97d490530e23476960ab8780ad86349947d82f1c9e57c85f86d71f80a6709b58be5f993a6a6df80c5a0857627d4a01e71484f6a6e983985089c00fe538e947230813c3a3e19baf6dae6db7082d07392a239ec1be385646356db3e3d76571488a6c72f0b96997f6191beea9846fc99f82a828f05af95cfc234cf681002f830915b1f3d35b2178b54a861c05d2694c5f6cfeb613a4a3670d849180461cdedf2c3cbb022608d8b19c86179d2d6da6b9acefccfc34b59663ef1282fec262bef79b2fbdd9b6669c90d6817b3762164dc309616469b33b83b1ded3420ae9177bc8f456d83939ff3c91b0a3683f3157401ceadf679c9f876da2aa413e081ee4c41d4b04f49e0c254d0082fd9bf2cb8eb8b966285be2cdcaab0ab70ea970737244b6683283598c30bdc206a05df72048b342eb40c2cd750c815d5fa944167b103ec40d60a99c49941a9e76d874149524f35ca294d081cf221757df77e027640556d983978be6b4b51aff26cd74a2f300d71chef-12.3.0/spec/data/partial_one.erb0000644000004100000410000000012212520074675017367 0ustar www-datawww-datapartial one <%= render('test.erb', :cookbook => 'openldap').strip %> calling home chef-12.3.0/spec/data/recipes/0000755000004100000410000000000012520074675016037 5ustar www-datawww-datachef-12.3.0/spec/data/recipes/test.rb0000644000004100000410000000014312520074675017341 0ustar www-datawww-data file "/etc/nsswitch.conf" do action :create owner "root" group "root" mode 0644 end chef-12.3.0/spec/rcov.opts0000644000004100000410000000004112520074675015347 0ustar www-datawww-data--exclude spec,bin,/Library/Ruby chef-12.3.0/spec/spec_helper.rb0000644000004100000410000001751012520074675016316 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # If you need to add anything in here, don't. # Add it to one of the files in spec/support # Abuse ruby's constant lookup to avoid undefined constant errors module Shell JUST_TESTING_MOVE_ALONG = true unless defined? JUST_TESTING_MOVE_ALONG IRB = nil unless defined? IRB end # Ruby 1.9 Compat $:.unshift File.expand_path("../..", __FILE__) require 'rubygems' require 'rspec/mocks' $:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) $:.unshift(File.expand_path("../lib", __FILE__)) $:.unshift(File.dirname(__FILE__)) if ENV["COVERAGE"] require 'simplecov' SimpleCov.start do add_filter "/spec/" add_group "Remote File", "remote_file" add_group "Resources", "/resource/" add_group "Providers", "/provider/" add_group "Knife", "knife" end end require 'chef' require 'chef/knife' Dir['lib/chef/knife/**/*.rb']. map {|f| f.gsub('lib/', '') }. map {|f| f.gsub(%r[\.rb$], '') }. each {|f| require f } require 'chef/mixins' require 'chef/dsl' require 'chef/application' require 'chef/applications' require 'chef/shell' require 'chef/util/file_edit' require 'chef/config' # If you want to load anything into the testing environment # without versioning it, add it to spec/support/local_gems.rb require 'spec/support/local_gems.rb' if File.exists?(File.join(File.dirname(__FILE__), 'support', 'local_gems.rb')) # Explicitly require spec helpers that need to load first require 'spec/support/platform_helpers' # Autoloads support files # Excludes support/platforms by default # Do not change the gsub. Dir["spec/support/**/*.rb"]. reject { |f| f =~ %r{^spec/support/platforms} }. reject { |f| f =~ %r{^spec/support/pedant} }. map { |f| f.gsub(%r{.rb$}, '') }. map { |f| f.gsub(%r[spec/], '')}. each { |f| require f } OHAI_SYSTEM = Ohai::System.new OHAI_SYSTEM.all_plugins("platform") TEST_PLATFORM = (OHAI_SYSTEM['platform'] || 'unknown_test_platform').dup.freeze TEST_PLATFORM_VERSION = (OHAI_SYSTEM['platform_version'] || 'unknown_platform_version').dup.freeze RSpec.configure do |config| config.include(Matchers) config.filter_run :focus => true config.filter_run_excluding :external => true # Explicitly disable :should syntax config.expect_with :rspec do |c| c.syntax = :expect end config.mock_with :rspec do |c| c.syntax = :expect end # Only run these tests on platforms that are also chef workstations config.filter_run_excluding :workstation if solaris? or aix? # Tests that randomly fail, but may have value. config.filter_run_excluding :volatile => true config.filter_run_excluding :volatile_on_solaris => true if solaris? config.filter_run_excluding :volatile_from_verify => false # Add jruby filters here config.filter_run_excluding :windows_only => true unless windows? config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106? config.filter_run_excluding :not_supported_on_mac_osx=> true if mac_osx? config.filter_run_excluding :mac_osx_only=> true if !mac_osx? config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3? config.filter_run_excluding :not_supported_on_solaris => true if solaris? config.filter_run_excluding :win2k3_only => true unless windows_win2k3? config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later? config.filter_run_excluding :windows64_only => true unless windows64? config.filter_run_excluding :windows32_only => true unless windows32? config.filter_run_excluding :windows_powershell_dsc_only => true unless windows_powershell_dsc? config.filter_run_excluding :windows_powershell_no_dsc_only => true unless ! windows_powershell_dsc? config.filter_run_excluding :windows_domain_joined_only => true unless windows_domain_joined? config.filter_run_excluding :solaris_only => true unless solaris? config.filter_run_excluding :system_windows_service_gem_only => true unless system_windows_service_gem? config.filter_run_excluding :unix_only => true unless unix? config.filter_run_excluding :aix_only => true unless aix? config.filter_run_excluding :supports_cloexec => true unless supports_cloexec? config.filter_run_excluding :selinux_only => true unless selinux_enabled? config.filter_run_excluding :ruby_20_only => true unless ruby_20? # chef_gte_XX_only and chef_lt_XX_only pair up correctly with the same XX # number. please conserve this pattern & resist filling out all the operators config.filter_run_excluding :chef_gte_13_only => true unless chef_gte_13? config.filter_run_excluding :chef_lt_13_only => true unless chef_lt_13? config.filter_run_excluding :requires_root => true unless root? config.filter_run_excluding :requires_root_or_running_windows => true unless (root? || windows?) config.filter_run_excluding :requires_unprivileged_user => true if root? config.filter_run_excluding :uses_diff => true unless has_diff? config.filter_run_excluding :openssl_gte_101 => true unless openssl_gte_101? config.filter_run_excluding :openssl_lt_101 => true unless openssl_lt_101? config.filter_run_excluding :aes_256_gcm_only => true unless aes_256_gcm? config.filter_run_excluding :broken => true running_platform_arch = `uname -m`.strip config.filter_run_excluding :arch => lambda {|target_arch| running_platform_arch != target_arch } # Functional Resource tests that are provider-specific: # context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #... config.filter_run_excluding :provider => lambda {|criteria| type, target_provider = criteria.first platform = TEST_PLATFORM.dup platform_version = TEST_PLATFORM_VERSION.dup begin provider_for_running_platform = Chef::Platform.find_provider(platform, platform_version, type) provider_for_running_platform != target_provider rescue ArgumentError # no provider for platform true end } config.run_all_when_everything_filtered = true config.before(:each) do Chef::Config.reset # By default, treat deprecation warnings as errors in tests. Chef::Config.treat_deprecation_warnings_as_errors(true) # Set environment variable so the setting persists in child processes ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS'] = "1" end config.before(:suite) do ARGV.clear end end require 'webrick/utils' # Webrick uses a centralized/synchronized timeout manager. It works by # starting a thread to check for timeouts on an interval. The timeout # checker thread cannot be stopped or canceled in any easy way, and it # makes calls to Time.new, which fail when rspec is in the process of # creating a method stub for that method. Since our tests don't rely on # any timeout behavior enforced by webrick, disable the timeout manager # via a monkey patch. # # Hopefully this fails loudly if the webrick code should change. As of this # writing, the relevant code is in webrick/utils, which can be located on # your system with: # # $ gem which webrick/utils module WEBrick module Utils class TimeoutHandler def initialize @timeout_info = Hash.new end end end end # Enough stuff needs json serialization that I'm just adding it here for equality asserts require 'chef/json_compat' chef-12.3.0/spec/scripts/0000755000004100000410000000000012520074675015163 5ustar www-datawww-datachef-12.3.0/spec/scripts/ssl-serve.rb0000644000004100000410000000255112520074675017436 0ustar www-datawww-data# ssl-serve.rb # USAGE: ruby ssl-serve.rb # # ssl-serve is a script that serves a local directory over SSL. # You can use it to test various HTTP behaviors in chef, like chef-client's # `-j` and `-c` options and remote_file with https connections. # require 'pp' require 'openssl' require 'webrick' require 'webrick/https' $ssl = true CHEF_SPEC_DATA = File.expand_path("../../data", __FILE__) cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA)) cert = OpenSSL::X509::Certificate.new(cert_text) key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA)) key = OpenSSL::PKey::RSA.new(key_text) server_opts = {} if $ssl server_opts.merge!( { :SSLEnable => true, :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE, :SSLCertificate => cert, :SSLPrivateKey => key }) end # 5 == debug, 3 == warning LOGGER = WEBrick::Log.new(STDOUT, 5) DEFAULT_OPTIONS = { :server => 'webrick', :Port => 9000, :Host => 'localhost', :environment => :none, :Logger => LOGGER, :DocumentRoot => File.expand_path("/tmp/chef-118-sampledata") #:AccessLog => [] # Remove this option to enable the access log when debugging. } webrick_opts = DEFAULT_OPTIONS.merge(server_opts) pp :webrick_opts => webrick_opts server = WEBrick::HTTPServer.new(webrick_opts) trap 'INT' do server.shutdown end server.start chef-12.3.0/spec/tiny_server.rb0000644000004100000410000001167512520074675016404 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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' require 'webrick' require 'webrick/https' require 'rack' #require 'thin' require 'singleton' require 'open-uri' require 'chef/config' module TinyServer class Server < Rack::Server attr_writer :app def self.setup(options=nil, &block) tiny_app = new(options) app_code = Rack::Builder.new(&block).to_app tiny_app.app = app_code tiny_app end def shutdown server.shutdown end end class Manager # 5 == debug, 3 == warning LOGGER = WEBrick::Log.new(STDOUT, 3) DEFAULT_OPTIONS = { :server => 'webrick', :Port => 9000, :Host => 'localhost', :environment => :none, :Logger => LOGGER, :AccessLog => [] # Remove this option to enable the access log when debugging. } def initialize(options=nil) @options = options ? DEFAULT_OPTIONS.merge(options) : DEFAULT_OPTIONS @creator = caller.first end def start @server_thread = Thread.new do @server = Server.setup(@options) do run API.instance end @server.start end block_until_started end def url "http://localhost:#{@options[:Port]}" end def block_until_started 200.times do if started? && !@server.nil? return true end end raise "ivar weirdness" if started? && @server.nil? raise "TinyServer failed to boot :/" end def started? open(url) true rescue OpenURI::HTTPError true rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET => e sleep 0.1 # If the host has ":::1 localhost" in its hosts file and if IPv6 # is not enabled we can get NetworkUnreachable exception... rescue Errno::ENETUNREACH => e sleep 0.1 false end def stop # yes, this is terrible. @server.shutdown @server_thread.kill @server_thread.join @server_thread = nil end end class API include Singleton GET = "GET" PUT = "PUT" POST = "POST" DELETE = "DELETE" attr_reader :routes def initialize clear end def clear @routes = {GET => [], PUT => [], POST => [], DELETE => []} end def get(path, response_code, data=nil, headers=nil, &block) @routes[GET] << Route.new(path, Response.new(response_code, data, headers, &block)) end def put(path, response_code, data=nil, headers=nil, &block) @routes[PUT] << Route.new(path, Response.new(response_code, data, headers, &block)) end def post(path, response_code, data=nil, headers=nil, &block) @routes[POST] << Route.new(path, Response.new(response_code, data, headers, &block)) end def delete(path, response_code, data=nil, headers=nil, &block) @routes[DELETE] << Route.new(path, Response.new(response_code, data, headers, &block)) end def call(env) if response = response_for_request(env) response.call else debug_info = {:message => "no data matches the request for #{env['REQUEST_URI']}", :available_routes => @routes, :request => env} # Uncomment me for glorious debugging # pp :not_found => debug_info [404, {'Content-Type' => 'application/json'}, [ Chef::JSONCompat.to_json(debug_info) ]] end end def response_for_request(env) if route = @routes[env["REQUEST_METHOD"]].find { |route| route.matches_request?(env["REQUEST_URI"]) } route.response end end end class Route attr_reader :response def initialize(path_spec, response) @path_spec, @response = path_spec, response end def matches_request?(uri) uri = URI.parse(uri).request_uri @path_spec === uri end def to_s "#{@path_spec} => (#{@response})" end end class Response HEADERS = {'Content-Type' => 'application/json'} def initialize(response_code=200, data=nil, headers=nil, &block) @response_code, @data = response_code, data @response_headers = headers ? HEADERS.merge(headers) : HEADERS @block = block_given? ? block : nil end def call data = @data || @block.call [@response_code, @response_headers, Array(data)] end def to_s "#{@response_code} => #{(@data|| @block)}" end end end chef-12.3.0/spec/stress/0000755000004100000410000000000012520074675015017 5ustar www-datawww-datachef-12.3.0/spec/stress/win32/0000755000004100000410000000000012520074675015761 5ustar www-datawww-datachef-12.3.0/spec/stress/win32/memory_spec.rb0000644000004100000410000000141412520074675020630 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' describe 'Chef::ReservedNames::Win32::Memory', :windows_only do end chef-12.3.0/spec/stress/win32/security_spec.rb0000644000004100000410000000453012520074675021171 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' if windows? require 'chef/win32/security' require 'tmpdir' require 'fileutils' end describe 'Chef::ReservedNames::Win32::Security', :windows_only do def monkeyfoo File.join(CHEF_SPEC_DATA, "monkeyfoo").gsub("/", "\\") end before :all do @test_tempdir = File.join(Dir::tmpdir, "cheftests", "chef_win32_security") FileUtils.mkdir_p(@test_tempdir) @monkeyfoo = File.join(@test_tempdir, "monkeyfoo.txt") end before :each do File.delete(@monkeyfoo) if File.exist?(@monkeyfoo) # Make a file. File.open(@monkeyfoo, "w") do |file| file.write("hi") end end after :all do FileUtils.rm_rf(@test_tempdir) end it "should not leak when retrieving and reading the ACE from a file", :volatile do expect { sids = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo).security_descriptor.dacl.select { |ace| ace.sid } GC.start }.not_to leak_memory(:warmup => 50, :iterations => 100) end it "should not leak when creating a new ACL and setting it on a file", :volatile do securable_object = Security::SecurableObject.new(@monkeyfoo) expect { securable_object.dacl = Chef::ReservedNames::Win32::Security::ACL.create([ Chef::ReservedNames::Win32::Security::ACE.access_allowed(Chef::ReservedNames::Win32::Security::SID.Everyone, Chef::ReservedNames::Win32::API::Security::GENERIC_READ), Chef::ReservedNames::Win32::Security::ACE.access_denied(Chef::ReservedNames::Win32::Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) ]) GC.start }.not_to leak_memory(:warmup => 50, :iterations => 100) end end chef-12.3.0/spec/stress/win32/file_spec.rb0000644000004100000410000000252312520074675020241 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' require 'chef/win32/file' if windows? describe 'Chef::ReservedNames::Win32::File', :windows_only do before(:each) do @path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "old_home_dir", "my-dot-emacs")) end it "should not leak significant memory", :volatile do test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) } expect(test).not_to leak_memory(:warmup => 50000, :iterations => 50000) end it "should not leak handles", :volatile do test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) } expect(test).not_to leak_handles(:warmup => 50, :iterations => 100) end end chef-12.3.0/spec/integration/0000755000004100000410000000000012520074675016017 5ustar www-datawww-datachef-12.3.0/spec/integration/client/0000755000004100000410000000000012520074675017275 5ustar www-datawww-datachef-12.3.0/spec/integration/client/ipv6_spec.rb0000644000004100000410000001146212520074675021524 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'support/shared/integration/integration_helper' require 'chef/mixin/shell_out' describe "chef-client" do include IntegrationSupport include Chef::Mixin::ShellOut let(:chef_zero_opts) { {:host => "::1"} } let(:validation_pem) do <<-END_VALIDATION_PEM -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2 V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA= -----END RSA PRIVATE KEY----- END_VALIDATION_PEM end let(:cache_path) do Dir.mktmpdir end let(:basic_config_file) do <<-END_CLIENT_RB chef_server_url "http://[::1]:8900" validation_key '#{path_to('config/validator.pem')}' cache_path '#{cache_path}' client_key '#{cache_path}/client.pem' END_CLIENT_RB end let(:client_rb_content) do basic_config_file end let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") } let(:chef_client_cmd) { %Q[ruby '#{chef_dir}/chef-client' --minimal-ohai -c "#{path_to('config/client.rb')}" -lwarn] } after do FileUtils.rm_rf(cache_path) end # Some Solaris test platforms are too old for IPv6. These tests should not # otherwise be platform dependent, so exclude solaris when_the_chef_server "is running on IPv6", :not_supported_on_solaris do when_the_repository "has a cookbook with a no-op recipe" do before do cookbook 'noop', '1.0.0', { }, "recipes" => {"default.rb" => "#raise 'foo'"} file 'config/client.rb', client_rb_content file 'config/validator.pem', validation_pem end it "should complete with success" do result = shell_out("#{chef_client_cmd} -o 'noop::default'", :cwd => chef_dir) result.error! end end when_the_repository "has a cookbook that hits server APIs" do before do recipe=<<-END_RECIPE actual_item = data_bag_item("expect_bag", "expect_item") if actual_item.key?("expect_key") and actual_item["expect_key"] == "expect_value" Chef::Log.info "lookin good" else Chef::Log.error("!" * 80) raise "unexpected data bag item content \#{actual_item.inspect}" Chef::Log.error("!" * 80) end END_RECIPE data_bag('expect_bag', { 'expect_item' => {"expect_key" => "expect_value"} }) cookbook 'api-smoke-test', '1.0.0', { }, "recipes" => {"default.rb" => recipe} end before do file 'config/client.rb', client_rb_content file 'config/validator.pem', validation_pem end it "should complete with success" do result = shell_out("#{chef_client_cmd} -o 'api-smoke-test::default'", :cwd => chef_dir) result.error! end end end end chef-12.3.0/spec/integration/client/client_spec.rb0000644000004100000410000002711312520074675022116 0ustar www-datawww-datarequire 'support/shared/integration/integration_helper' require 'chef/mixin/shell_out' require 'tiny_server' require 'tmpdir' def recipes_filename File.join(CHEF_SPEC_DATA, 'recipes.tgz') end def start_tiny_server(server_opts={}) recipes_size = File::Stat.new(recipes_filename).size @server = TinyServer::Manager.new(server_opts) @server.start @api = TinyServer::API.instance @api.clear # # trivial endpoints # # just a normal file # (expected_content should be uncompressed) @api.get("/recipes.tgz", 200) { File.open(recipes_filename, "rb") do |f| f.read end } end def stop_tiny_server @server.stop @server = @api = nil end describe "chef-client" do include IntegrationSupport include Chef::Mixin::ShellOut let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") } # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the # following constraints are satisfied: # * Windows: windows can only run batch scripts as bare executables. Rubygems # creates batch wrappers for installed gems, but we don't have batch wrappers # in the source tree. # * Other `chef-client` in PATH: A common case is running the tests on a # machine that has omnibus chef installed. In that case we need to ensure # we're running `chef-client` from the source tree and not the external one. # cf. CHEF-4914 let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" } when_the_repository "has a cookbook with a no-op recipe" do before { file 'cookbooks/x/recipes/default.rb', '' } it "should complete with success" do file 'config/client.rb', < chef_dir) result.error! end it "should complete successfully with --no-listen" do file 'config/client.rb', < chef_dir) result.error! end context 'and no config file' do it 'should complete with success when cwd is just above cookbooks and paths are not specified' do result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => path_to('')) result.error! end it 'should complete with success when cwd is below cookbooks and paths are not specified' do result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => path_to('cookbooks/x')) result.error! end it 'should fail when cwd is below high above and paths are not specified' do result = shell_out("#{chef_client} -z -o 'x::default' --disable-config", :cwd => File.expand_path('..', path_to(''))) expect(result.exitstatus).to eq(1) end end context 'and a config file under .chef/knife.rb' do before { file '.chef/knife.rb', 'xxx.xxx' } it 'should load .chef/knife.rb when -z is specified' do result = shell_out("#{chef_client} -z -o 'x::default'", :cwd => path_to('')) # FATAL: Configuration error NoMethodError: undefined method `xxx' for nil:NilClass expect(result.stdout).to include("xxx") end end it "should complete with success" do file 'config/client.rb', < chef_dir) result.error! end context 'and a private key' do before do file 'mykey.pem', < chef_dir) result.error! end it "should run recipes specified directly on the command line" do file 'config/client.rb', < chef_dir) result.error! expect(IO.read(path_to('tempfile.txt'))).to eq('1') expect(IO.read(path_to('tempfile2.txt'))).to eq('2') end it "should run recipes specified as relative paths directly on the command line" do file 'config/client.rb', < path_to('')) result.error! expect(IO.read(path_to('tempfile.txt'))).to eq('1') end it "should run recipes specified directly on the command line AFTER recipes in the run list" do file 'config/client.rb', < path_to('')) result.error! expect(IO.read(path_to('tempfile.txt'))).to eq('1') end end it "should complete with success when passed the -z flag" do file 'config/client.rb', < chef_dir) result.error! end it "should complete with success when passed the --local-mode flag" do file 'config/client.rb', < chef_dir) result.error! end it "should not print SSL warnings when running in local-mode" do file 'config/client.rb', < chef_dir) expect(result.stdout).not_to include("SSL validation of HTTPS requests is disabled.") result.error! end it "should complete with success when passed -z and --chef-zero-port" do file 'config/client.rb', < chef_dir) result.error! end it "should complete with success when setting the run list with -r" do file 'config/client.rb', < chef_dir) expect(result.stdout).not_to include("Overridden Run List") expect(result.stdout).to include("Run List is [recipe[x::default]]") #puts result.stdout result.error! end end when_the_repository "has a cookbook with only an audit recipe" do before do file 'config/client.rb', < chef_dir) expect(result.error?).to be_falsey expect(result.stdout).to include("Successfully executed all `control_group` blocks and contained examples") end it "should exit with a non-zero code when there is an audit failure" do file 'cookbooks/audit_test/recipes/fail.rb', <<-RECIPE control_group "control group without top level control" do it "should fail" do expect(2 - 2).to eq(1) end end RECIPE result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::fail'", :cwd => chef_dir) expect(result.error?).to be_truthy expect(result.stdout).to include("Failure/Error: expect(2 - 2).to eq(1)") end end context "when using recipe-url" do before(:all) do start_tiny_server end after(:all) do stop_tiny_server end let(:tmp_dir) { Dir.mktmpdir("recipe-url") } it "should complete with success when passed -z and --recipe-url" do file 'config/client.rb', < tmp_dir) result.error! end it 'should fail when passed --recipe-url and not passed -z' do result = shell_out("#{chef_client} --recipe-url=http://localhost:9000/recipes.tgz", :cwd => tmp_dir) expect(result.exitstatus).to eq(1) end end end chef-12.3.0/spec/integration/knife/0000755000004100000410000000000012520074675017113 5ustar www-datawww-datachef-12.3.0/spec/integration/knife/common_options_spec.rb0000644000004100000410000001370212520074675023520 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/raw' describe 'knife common options', :workstation do include IntegrationSupport include KnifeSupport when_the_repository "has a node" do before { file 'nodes/x.json', {} } context 'When chef_zero.enabled is true' do before(:each) do Chef::Config.chef_zero.enabled = true end it 'knife raw /nodes/x should retrieve the node' do knife('raw /nodes/x').should_succeed( /"name": "x"/ ) end context 'And chef_zero.port is 9999' do before(:each) { Chef::Config.chef_zero.port = 9999 } it 'knife raw /nodes/x should retrieve the node' do knife('raw /nodes/x').should_succeed( /"name": "x"/ ) expect(Chef::Config.chef_server_url).to eq('chefzero://localhost:9999') end end # 0.0.0.0 is not a valid address to bind to on windows. context 'And chef_zero.host is 0.0.0.0', :unix_only do before(:each) { Chef::Config.chef_zero.host = '0.0.0.0' } it 'knife raw /nodes/x should retrieve the role' do knife('raw /nodes/x').should_succeed( /"name": "x"/ ) end end context 'and there is a private key' do before do file 'mykey.pem', < 'localhost', :port => 8889) @server.start_background rescue Errno::EADDRINUSE # OK. Don't care who has it in use, as long as *someone* does. end end after :each do @server.stop if @server end it 'knife raw -z /nodes/x retrieves the node' do knife('raw -z /nodes/x').should_succeed( /"name": "x"/ ) expect(URI(Chef::Config.chef_server_url).port).to be > 8889 end end context 'when port 9999 is already bound' do before :each do begin @server = ChefZero::Server.new(:host => 'localhost', :port => 9999) @server.start_background rescue Errno::EADDRINUSE # OK. Don't care who has it in use, as long as *someone* does. end end after :each do @server.stop if @server end it 'knife raw -z --chef-zero-port=9999-20000 /nodes/x' do knife('raw -z --chef-zero-port=9999-20000 /nodes/x').should_succeed( /"name": "x"/ ) expect(URI(Chef::Config.chef_server_url).port).to be > 9999 end it 'knife raw -z --chef-zero-port=9999-9999,19423' do knife('raw -z --chef-zero-port=9999-9999,19423 /nodes/x').should_succeed( /"name": "x"/ ) expect(URI(Chef::Config.chef_server_url).port).to be == 19423 end end it 'knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node' do knife('raw -z --chef-zero-port=9999 /nodes/x').should_succeed( /"name": "x"/ ) expect(Chef::Config.chef_server_url).to eq('chefzero://localhost:9999') end end end chef-12.3.0/spec/integration/knife/serve_spec.rb0000644000004100000410000000320612520074675021577 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/serve' require 'chef/server_api' describe 'knife serve', :workstation do include IntegrationSupport include KnifeSupport include AppServerSupport when_the_repository 'also has one of each thing' do before { file 'nodes/x.json', { 'foo' => 'bar' } } it 'knife serve serves up /nodes/x' do exception = nil t = Thread.new do begin knife('serve --chef-zero-port=8890') rescue exception = $! end end begin Chef::Config.log_level = :debug Chef::Config.chef_server_url = 'http://localhost:8890' Chef::Config.node_name = nil Chef::Config.client_key = nil api = Chef::ServerAPI.new expect(api.get('nodes/x')['name']).to eq('x') rescue if exception raise exception else raise end ensure t.kill end end end end chef-12.3.0/spec/integration/knife/chef_fs_data_store_spec.rb0000644000004100000410000003411112520074675024254 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/list' require 'chef/knife/delete' require 'chef/knife/show' require 'chef/knife/raw' require 'chef/knife/cookbook_upload' describe 'ChefFSDataStore tests', :workstation do include IntegrationSupport include KnifeSupport let(:cookbook_x_100_metadata_rb) { cb_metadata("x", "1.0.0") } let(:cookbook_z_100_metadata_rb) { cb_metadata("z", "1.0.0") } when_the_repository "has one of each thing" do before do file 'clients/x.json', {} file 'cookbooks/x/metadata.rb', cookbook_x_100_metadata_rb file 'data_bags/x/y.json', {} file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/x.json', {} end context 'GET /TYPE' do it 'knife list -z -R returns everything' do knife('list -z -Rfp /').should_succeed < "x", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}} file 'rolestuff.json', '{"description":"hi there","name":"x"}' file 'cookbooks_to_upload/x/metadata.rb', cookbook_x_100_metadata_rb end it 'knife raw -z -i empty.json -m PUT /clients/x' do knife("raw -z -i #{path_to('empty.json')} -m PUT /clients/x").should_succeed( /"x"/ ) knife('list --local /clients').should_succeed "/clients/x.json\n" end it 'knife cookbook upload works' do knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed :stderr => < (RUBY_VERSION < "1.9") do knife("raw -z -i #{path_to('rolestuff.json')} -m PUT /roles/x").should_succeed( /"x"/ ) expect(IO.read(path_to('roles/x.json'))).to eq < 'z' } file 'dummynode.json', { "name" => "z", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}} file 'empty_x.json', { 'name' => 'x' } file 'empty_id.json', { 'id' => 'z' } file 'rolestuff.json', '{"description":"hi there","name":"x"}' file 'cookbooks_to_upload/z/metadata.rb', cookbook_z_100_metadata_rb end it 'knife raw -z -i empty.json -m POST /clients' do knife("raw -z -i #{path_to('empty.json')} -m POST /clients").should_succeed( /uri/ ) knife('list --local /clients').should_succeed "/clients/z.json\n" end it 'knife cookbook upload works' do knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed :stderr => < (RUBY_VERSION < "1.9") do knife("raw -z -i #{path_to('rolestuff.json')} -m POST /roles").should_succeed( /uri/ ) expect(IO.read(path_to('roles/x.json'))).to eq <) # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/deps' describe 'knife deps', :workstation do include IntegrationSupport include KnifeSupport context 'local' do when_the_repository 'has a role with no run_list' do before { file 'roles/starring.json', {} } it 'knife deps reports no dependencies' do knife('deps /roles/starring.json').should_succeed "/roles/starring.json\n" end end when_the_repository 'has a role with a default run_list' do before do file 'roles/starring.json', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } file 'roles/minor.json', {} file 'cookbooks/quiche/metadata.rb', 'name "quiche"' file 'cookbooks/quiche/recipes/default.rb', '' file 'cookbooks/soup/metadata.rb', 'name "soup"' file 'cookbooks/soup/recipes/chicken.rb', '' end it 'knife deps reports all dependencies' do knife('deps /roles/starring.json').should_succeed < { 'desert' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } } file 'roles/minor.json', {} file 'cookbooks/quiche/metadata.rb', 'name "quiche"' file 'cookbooks/quiche/recipes/default.rb', '' file 'cookbooks/soup/metadata.rb', 'name "soup"' file 'cookbooks/soup/recipes/chicken.rb', '' end it 'knife deps reports all dependencies' do knife('deps /roles/starring.json').should_succeed < 'desert' } end it 'knife deps reports just the node' do knife('deps /nodes/mort.json').should_succeed "/environments/desert.json\n/nodes/mort.json\n" end end when_the_repository 'has a node with roles and recipes in its run_list' do before do file 'roles/minor.json', {} file 'cookbooks/quiche/metadata.rb', 'name "quiche"' file 'cookbooks/quiche/recipes/default.rb', '' file 'cookbooks/soup/metadata.rb', 'name "soup"' file 'cookbooks/soup/recipes/chicken.rb', '' file 'nodes/mort.json', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } end it 'knife deps reports just the node' do knife('deps /nodes/mort.json').should_succeed < %w(role[minor] recipe[quiche] recipe[soup::chicken]) } file 'roles/minor.json', {} file 'cookbooks/quiche/metadata.rb', 'name "quiche"' file 'cookbooks/quiche/recipes/default.rb', '' file 'cookbooks/soup/metadata.rb', 'name "soup"' file 'cookbooks/soup/recipes/chicken.rb', '' file 'environments/desert.json', {} file 'nodes/mort.json', { 'chef_environment' => 'desert', 'run_list' => [ 'role[starring]' ] } file 'nodes/bart.json', { 'run_list' => [ 'role[minor]' ] } end it 'knife deps reports all dependencies' do knife('deps /nodes/mort.json').should_succeed < [ 'role[bar]' ] } file 'roles/bar.json', { 'run_list' => [ 'role[baz]' ] } file 'roles/baz.json', { 'run_list' => [ 'role[foo]' ] } file 'roles/self.json', { 'run_list' => [ 'role[self]' ] } end it 'knife deps prints each once' do knife('deps /roles/foo.json /roles/self.json').should_succeed < 2, :stdout => "/blah\n", :stderr => "ERROR: /blah: No such file or directory\n" ) end it 'knife deps /roles/x.json reports an error' do knife('deps /roles/x.json').should_fail( :exit_code => 2, :stdout => "/roles/x.json\n", :stderr => "ERROR: /roles/x.json: No such file or directory\n" ) end it 'knife deps /nodes/x.json reports an error' do knife('deps /nodes/x.json').should_fail( :exit_code => 2, :stdout => "/nodes/x.json\n", :stderr => "ERROR: /nodes/x.json: No such file or directory\n" ) end it 'knife deps /environments/x.json reports an error' do knife('deps /environments/x.json').should_fail( :exit_code => 2, :stdout => "/environments/x.json\n", :stderr => "ERROR: /environments/x.json: No such file or directory\n" ) end it 'knife deps /cookbooks/x reports an error' do knife('deps /cookbooks/x').should_fail( :exit_code => 2, :stdout => "/cookbooks/x\n", :stderr => "ERROR: /cookbooks/x: No such file or directory\n" ) end it 'knife deps /data_bags/bag/item reports an error' do knife('deps /data_bags/bag/item').should_fail( :exit_code => 2, :stdout => "/data_bags/bag/item\n", :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n" ) end end when_the_repository 'is missing a dependent cookbook' do before do file 'roles/starring.json', { 'run_list' => [ 'recipe[quiche]'] } end it 'knife deps reports the cookbook, along with an error' do knife('deps /roles/starring.json').should_fail( :exit_code => 2, :stdout => "/cookbooks/quiche\n/roles/starring.json\n", :stderr => "ERROR: /cookbooks/quiche: No such file or directory\n" ) end end when_the_repository 'is missing a dependent environment' do before do file 'nodes/mort.json', { 'chef_environment' => 'desert' } end it 'knife deps reports the environment, along with an error' do knife('deps /nodes/mort.json').should_fail( :exit_code => 2, :stdout => "/environments/desert.json\n/nodes/mort.json\n", :stderr => "ERROR: /environments/desert.json: No such file or directory\n" ) end end when_the_repository 'is missing a dependent role' do before do file 'roles/starring.json', { 'run_list' => [ 'role[minor]'] } end it 'knife deps reports the role, along with an error' do knife('deps /roles/starring.json').should_fail( :exit_code => 2, :stdout => "/roles/minor.json\n/roles/starring.json\n", :stderr => "ERROR: /roles/minor.json: No such file or directory\n" ) end end end context 'invalid objects' do when_the_repository 'is empty' do it 'knife deps / reports itself only' do knife('deps /').should_succeed("/\n") end it 'knife deps /roles reports an error' do knife('deps /roles').should_fail( :exit_code => 2, :stderr => "ERROR: /roles: No such file or directory\n", :stdout => "/roles\n" ) end end when_the_repository 'has a data bag' do before { file 'data_bags/bag/item.json', '' } it 'knife deps /data_bags/bag shows no dependencies' do knife('deps /data_bags/bag').should_succeed("/data_bags/bag\n") end end when_the_repository 'has a cookbook' do before { file 'cookbooks/blah/metadata.rb', 'name "blah"' } it 'knife deps on a cookbook file shows no dependencies' do knife('deps /cookbooks/blah/metadata.rb').should_succeed( "/cookbooks/blah/metadata.rb\n" ) end end end end context 'remote' do include_context "default config options" when_the_chef_server 'has a role with no run_list' do before { role 'starring', {} } it 'knife deps reports no dependencies' do knife('deps --remote /roles/starring.json').should_succeed "/roles/starring.json\n" end end when_the_chef_server 'has a role with a default run_list' do before do role 'starring', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } role 'minor', {} cookbook 'quiche', '1.0.0', { 'metadata.rb' => %Q{name "quiche"\nversion "1.0.0"\n}, 'recipes' => { 'default.rb' => '' } } cookbook 'soup', '1.0.0', { 'metadata.rb' => %Q{name "soup"\nversion "1.0.0"\n}, 'recipes' => { 'chicken.rb' => '' } } end it 'knife deps reports all dependencies' do knife('deps --remote /roles/starring.json').should_succeed < { 'desert' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } } role 'minor', {} cookbook 'quiche', '1.0.0', { 'metadata.rb' => %Q{name "quiche"\nversion "1.0.0"\n}, 'recipes' => { 'default.rb' => '' } } cookbook 'soup', '1.0.0', { 'metadata.rb' => %Q{name "soup"\nversion "1.0.0"\n}, 'recipes' => { 'chicken.rb' => '' } } end it 'knife deps reports all dependencies' do knife('deps --remote /roles/starring.json').should_succeed < 'desert' } end it 'knife deps reports just the node' do knife('deps --remote /nodes/mort.json').should_succeed "/environments/desert.json\n/nodes/mort.json\n" end end when_the_chef_server 'has a node with roles and recipes in its run_list' do before do role 'minor', {} cookbook 'quiche', '1.0.0', { 'metadata.rb' => %Q{name "quiche"\nversion "1.0.0"\n}, 'recipes' => { 'default.rb' => '' } } cookbook 'soup', '1.0.0', { 'metadata.rb' => %Q{name "soup"\nversion "1.0.0"\n}, 'recipes' => { 'chicken.rb' => '' } } node 'mort', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } end it 'knife deps reports just the node' do knife('deps --remote /nodes/mort.json').should_succeed < %Q{name "quiche"\nversion "1.0.0"\n}, 'recipes' => { 'default.rb' => '' } } end it 'knife deps reports just the cookbook' do knife('deps --remote /cookbooks/quiche').should_succeed "/cookbooks/quiche\n" end end when_the_chef_server 'has a cookbook with dependencies' do before do cookbook 'kettle', '1.0.0', { 'metadata.rb' => %Q{name "kettle"\nversion "1.0.0"\n} } cookbook 'quiche', '1.0.0', { 'metadata.rb' => 'name "quiche" depends "kettle"', 'recipes' => { 'default.rb' => '' } } end it 'knife deps reports the cookbook and its dependencies' do knife('deps --remote /cookbooks/quiche').should_succeed "/cookbooks/kettle\n/cookbooks/quiche\n" end end when_the_chef_server 'has a data bag' do before { data_bag 'bag', { 'item' => {} } } it 'knife deps reports just the data bag' do knife('deps --remote /data_bags/bag/item.json').should_succeed "/data_bags/bag/item.json\n" end end when_the_chef_server 'has an environment' do before { environment 'desert', {} } it 'knife deps reports just the environment' do knife('deps --remote /environments/desert.json').should_succeed "/environments/desert.json\n" end end when_the_chef_server 'has a deep dependency tree' do before do role 'starring', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } role 'minor', {} cookbook 'quiche', '1.0.0', { 'metadata.rb' => %Q{name "quiche"\nversion "1.0.0"\n}, 'recipes' => { 'default.rb' => '' } } cookbook 'soup', '1.0.0', { 'metadata.rb' => %Q{name "soup"\nversion "1.0.0"\n}, 'recipes' => { 'chicken.rb' => '' } } environment 'desert', {} node 'mort', { 'chef_environment' => 'desert', 'run_list' => [ 'role[starring]' ] } node 'bart', { 'run_list' => [ 'role[minor]' ] } end it 'knife deps reports all dependencies' do knife('deps --remote /nodes/mort.json').should_succeed < 'name "foo" depends "bar"' } cookbook 'bar', '1.0.0', { 'metadata.rb' => 'name "bar" depends "baz"' } cookbook 'baz', '1.0.0', { 'metadata.rb' => 'name "baz" depends "foo"' } cookbook 'self', '1.0.0', { 'metadata.rb' => 'name "self" depends "self"' } end it 'knife deps prints each once' do knife('deps --remote /cookbooks/foo /cookbooks/self').should_succeed < [ 'role[bar]' ] } role 'bar', { 'run_list' => [ 'role[baz]' ] } role 'baz', { 'run_list' => [ 'role[foo]' ] } role 'self', { 'run_list' => [ 'role[self]' ] } end it 'knife deps prints each once' do knife('deps --remote /roles/foo.json /roles/self.json').should_succeed < 2, :stdout => "/blah\n", :stderr => "ERROR: /blah: No such file or directory\n" ) end it 'knife deps /roles/x.json reports an error' do knife('deps --remote /roles/x.json').should_fail( :exit_code => 2, :stdout => "/roles/x.json\n", :stderr => "ERROR: /roles/x.json: No such file or directory\n" ) end it 'knife deps /nodes/x.json reports an error' do knife('deps --remote /nodes/x.json').should_fail( :exit_code => 2, :stdout => "/nodes/x.json\n", :stderr => "ERROR: /nodes/x.json: No such file or directory\n" ) end it 'knife deps /environments/x.json reports an error' do knife('deps --remote /environments/x.json').should_fail( :exit_code => 2, :stdout => "/environments/x.json\n", :stderr => "ERROR: /environments/x.json: No such file or directory\n" ) end it 'knife deps /cookbooks/x reports an error' do knife('deps --remote /cookbooks/x').should_fail( :exit_code => 2, :stdout => "/cookbooks/x\n", :stderr => "ERROR: /cookbooks/x: No such file or directory\n" ) end it 'knife deps /data_bags/bag/item reports an error' do knife('deps --remote /data_bags/bag/item').should_fail( :exit_code => 2, :stdout => "/data_bags/bag/item\n", :stderr => "ERROR: /data_bags/bag/item: No such file or directory\n" ) end end when_the_chef_server 'is missing a dependent cookbook' do before do role 'starring', { 'run_list' => [ 'recipe[quiche]'] } end it 'knife deps reports the cookbook, along with an error' do knife('deps --remote /roles/starring.json').should_fail( :exit_code => 2, :stdout => "/cookbooks/quiche\n/roles/starring.json\n", :stderr => "ERROR: /cookbooks/quiche: No such file or directory\n" ) end end when_the_chef_server 'is missing a dependent environment' do before do node 'mort', { 'chef_environment' => 'desert' } end it 'knife deps reports the environment, along with an error' do knife('deps --remote /nodes/mort.json').should_fail( :exit_code => 2, :stdout => "/environments/desert.json\n/nodes/mort.json\n", :stderr => "ERROR: /environments/desert.json: No such file or directory\n" ) end end when_the_chef_server 'is missing a dependent role' do before do role 'starring', { 'run_list' => [ 'role[minor]'] } end it 'knife deps reports the role, along with an error' do knife('deps --remote /roles/starring.json').should_fail( :exit_code => 2, :stdout => "/roles/minor.json\n/roles/starring.json\n", :stderr => "ERROR: /roles/minor.json: No such file or directory\n" ) end end end context 'invalid objects' do when_the_chef_server 'is empty' do it 'knife deps / reports an error' do knife('deps --remote /').should_succeed("/\n") end it 'knife deps /roles reports an error' do knife('deps --remote /roles').should_succeed("/roles\n") end end when_the_chef_server 'has a data bag' do before { data_bag 'bag', { 'item' => {} } } it 'knife deps /data_bags/bag shows no dependencies' do knife('deps --remote /data_bags/bag').should_succeed("/data_bags/bag\n") end end when_the_chef_server 'has a cookbook' do before do cookbook 'blah', '1.0.0', { 'metadata.rb' => 'name "blah"' } end it 'knife deps on a cookbook file shows no dependencies' do knife('deps --remote /cookbooks/blah/metadata.rb').should_succeed( "/cookbooks/blah/metadata.rb\n" ) end end end end it 'knife deps --no-recurse reports an error' do knife('deps --no-recurse /').should_fail("ERROR: --no-recurse requires --tree\n") end end chef-12.3.0/spec/integration/knife/diff_spec.rb0000644000004100000410000004510512520074675021367 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/diff' describe 'knife diff', :workstation do include IntegrationSupport include KnifeSupport context 'without versioned cookbooks' do when_the_chef_server "has one of each thing" do before do client 'x', '{}' cookbook 'x', '1.0.0' data_bag 'x', { 'y' => '{}' } environment 'x', '{}' node 'x', '{}' role 'x', '{}' user 'x', '{}' end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife diff reports everything as deleted' do knife('diff --name-status /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/_default.json', { "description" => "The default Chef environment" } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife diff reports no differences' do knife('diff /').should_succeed '' end it 'knife diff /environments/nonexistent.json reports an error' do knife('diff /environments/nonexistent.json').should_fail "ERROR: /environments/nonexistent.json: No such file or directory on remote or local\n" end it 'knife diff /environments/*.txt reports an error' do knife('diff /environments/*.txt').should_fail "ERROR: /environments/*.txt: No such file or directory on remote or local\n" end context 'except the role file' do before do file 'roles/x.json', < ChefZero::PUBLIC_KEY } file 'cookbooks/x/blah.rb', '' file 'cookbooks/y/metadata.rb', cb_metadata("y", "1.0.0") file 'data_bags/x/z.json', {} file 'data_bags/y/zz.json', {} file 'environments/y.json', {} file 'nodes/y.json', {} file 'roles/y.json', {} file 'users/y.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife diff reports the new files as added' do knife('diff --name-status /').should_succeed < ''} cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => '' } end it 'knife diff /cookbooks/x shows differences' do knife('diff --name-status /cookbooks/x').should_succeed < '' } cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => '' } end it 'knife diff /cookbooks/x shows no differences' do knife('diff --name-status /cookbooks/x').should_succeed '' end end when_the_chef_server 'has a later version for the cookbook, and no current version' do before do cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => '' } end it 'knife diff /cookbooks/x shows the differences' do knife('diff --name-status /cookbooks/x').should_succeed < '' } end it 'knife diff /cookbooks/x shows the differences' do knife('diff --name-status /cookbooks/x').should_succeed < 'hi' } } it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { - "name": "x", - "description": "hi" \+ "name": "x" } /) end end end when_the_repository 'has an environment file with a value in it' do before do file 'environments/x.json', { 'description' => 'hi' } end when_the_chef_server 'has an environment with the same value' do before do environment 'x', { 'description' => 'hi' } end it 'knife diff returns no differences' do knife('diff /environments/x.json').should_succeed '' end end when_the_chef_server 'has an environment with no value' do before do environment 'x', {} end it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { - "name": "x" \+ "name": "x", \+ "description": "hi" } /) end end when_the_chef_server 'has an environment with a different value' do before do environment 'x', { 'description' => 'lo' } end it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { "name": "x", - "description": "lo" \+ "description": "hi" } /) end end end end when_the_chef_server 'has an environment' do before { environment 'x', {} } when_the_repository 'has an environment with bad JSON' do before { file 'environments/x.json', '{' } it 'knife diff reports an error and does a textual diff' do error_text = "WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF" error_match = Regexp.new(Regexp.escape(error_text)) knife('diff /environments/x.json').should_succeed(/- "name": "x"/, :stderr => error_match) end end end end # without versioned cookbooks with_versioned_cookbooks do when_the_chef_server "has one of each thing" do before do client 'x', '{}' cookbook 'x', '1.0.0' data_bag 'x', { 'y' => '{}' } environment 'x', '{}' node 'x', '{}' role 'x', '{}' user 'x', '{}' end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife diff reports everything as deleted' do knife('diff --name-status /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x-1.0.0/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/_default.json', { "description" => "The default Chef environment" } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife diff reports no differences' do knife('diff /').should_succeed '' end it 'knife diff /environments/nonexistent.json reports an error' do knife('diff /environments/nonexistent.json').should_fail "ERROR: /environments/nonexistent.json: No such file or directory on remote or local\n" end it 'knife diff /environments/*.txt reports an error' do knife('diff /environments/*.txt').should_fail "ERROR: /environments/*.txt: No such file or directory on remote or local\n" end context 'except the role file' do before do file 'roles/x.json', < ''} cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => '' } end it 'knife diff /cookbooks shows differences' do knife('diff --name-status /cookbooks').should_succeed < '' } cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => '' } end it 'knife diff /cookbooks shows the differences' do knife('diff --name-status /cookbooks').should_succeed "D\t/cookbooks/x-0.9.9\n" end end when_the_chef_server 'has a later version for the cookbook, and no current version' do before do cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => '' } end it 'knife diff /cookbooks shows the differences' do knife('diff --name-status /cookbooks').should_succeed < '' } end it 'knife diff /cookbooks shows the differences' do knife('diff --name-status /cookbooks').should_succeed < 'hi' } } it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { - "name": "x", - "description": "hi" \+ "name": "x" } /) end end end when_the_repository 'has an environment file with a value in it' do before do file 'environments/x.json', { 'description' => 'hi' } end when_the_chef_server 'has an environment with the same value' do before do environment 'x', { 'description' => 'hi' } end it 'knife diff returns no differences' do knife('diff /environments/x.json').should_succeed '' end end when_the_chef_server 'has an environment with no value' do before { environment 'x', {} } it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { - "name": "x" \+ "name": "x", \+ "description": "hi" } /) end end when_the_chef_server 'has an environment with a different value' do before do environment 'x', { 'description' => 'lo' } end it 'knife diff reports the difference', :skip => (RUBY_VERSION < "1.9") do knife('diff /environments/x.json').should_succeed(/ { "name": "x", - "description": "lo" \+ "description": "hi" } /) end end end end when_the_chef_server 'has an environment' do before { environment 'x', {} } when_the_repository 'has an environment with bad JSON' do before { file 'environments/x.json', '{' } it 'knife diff reports an error and does a textual diff' do error_text = "WARN: Parse error reading #{path_to('environments/x.json')} as JSON: parse error: premature EOF" error_match = Regexp.new(Regexp.escape(error_text)) knife('diff /environments/x.json').should_succeed(/- "name": "x"/, :stderr => error_match) end end end end # without versioned cookbooks end chef-12.3.0/spec/integration/knife/chef_repo_path_spec.rb0000644000004100000410000006317612520074675023435 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/list' require 'chef/knife/show' describe 'chef_repo_path tests', :workstation do include IntegrationSupport include KnifeSupport # TODO alternate repo_path / *_path context 'alternate *_path' do when_the_repository 'has clients and clients2, cookbooks and cookbooks2, etc.' do before do file 'clients/client1.json', {} file 'cookbooks/cookbook1/metadata.rb', '' file 'data_bags/bag/item.json', {} file 'environments/env1.json', {} file 'nodes/node1.json', {} file 'roles/role1.json', {} file 'users/user1.json', {} file 'clients2/client2.json', {} file 'cookbooks2/cookbook2/metadata.rb', '' file 'data_bags2/bag2/item2.json', {} file 'environments2/env2.json', {} file 'nodes2/node2.json', {} file 'roles2/role2.json', {} file 'users2/user2.json', {} directory 'chef_repo2' do file 'clients/client3.json', {} file 'cookbooks/cookbook3/metadata.rb', '' file 'data_bags/bag3/item3.json', {} file 'environments/env3.json', {} file 'nodes/node3.json', {} file 'roles/role3.json', {} file 'users/user3.json', {} end end it 'knife list --local -Rfp --chef-repo-path chef_repo2 / grabs chef_repo2 stuff' do Chef::Config.delete(:chef_repo_path) knife("list --local -Rfp --chef-repo-path #{path_to('chef_repo2')} /").should_succeed < "WARN: Cookbook 'blah' is empty or entirely chefignored at #{Chef::Config.cookbook_path[0]}/blah\n") /cookbooks/blah/ /cookbooks/blah/metadata.rb /cookbooks/cookbook1/ /cookbooks/cookbook1/metadata.rb /cookbooks/cookbook2/ /cookbooks/cookbook2/metadata.rb EOM end end context 'when there is a cookbook in cookbooks1 and a cookbook in cookbooks2 with the same name' do before do file 'cookbooks/blah/metadata.json', {} file 'cookbooks2/blah/metadata.rb', '' end it 'knife list -Rfp cookbooks shows files in the first cookbook and not the second' do knife('list --local -Rfp /cookbooks').should_succeed(< "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.cookbook_path[0]}/blah and #{Chef::Config.cookbook_path[1]}/blah\n") /cookbooks/blah/ /cookbooks/blah/metadata.json /cookbooks/cookbook1/ /cookbooks/cookbook1/metadata.rb /cookbooks/cookbook2/ /cookbooks/cookbook2/metadata.rb EOM end end context 'when there is a file in data_bags1 and a directory in data_bags2 with the same name' do before do file 'data_bags/blah', '' file 'data_bags2/blah/item.json', '' end it 'knife list -Rfp data_bags shows files in blah' do knife('list --local -Rfp /data_bags').should_succeed < "WARN: Child with name 'blah' found in multiple directories: #{Chef::Config.data_bag_path[0]}/blah and #{Chef::Config.data_bag_path[1]}/blah\n") /data_bags/bag/ /data_bags/bag/item.json /data_bags/bag2/ /data_bags/bag2/item2.json /data_bags/blah/ /data_bags/blah/item1.json EOM end end context 'when there is a directory in environments1 and file in environments2 with the same name' do before do directory 'environments/blah.json' file 'environments2/blah.json', {} end it 'knife show /environments/blah.json succeeds' do knife('show --local /environments/blah.json').should_succeed <) # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/list' describe 'knife list', :workstation do include IntegrationSupport include KnifeSupport include_context "default config options" when_the_chef_server "is empty" do it "knife list / returns all top level directories" do knife('list /').should_succeed < { 'default.rb' => '' } } data_bag 'bag1', { 'item1' => {}, 'item2' => {} } data_bag 'bag2', { 'item1' => {}, 'item2' => {} } environment 'environment1', {} environment 'environment2', {} node 'node1', {} node 'node2', {} role 'role1', {} role 'role2', {} user 'user1', {} user 'user2', {} end it "knife list / returns all top level directories" do knife('list /').should_succeed < (Chef::Platform.windows?) do before do directory 'cookbooks' symlink 'symlinked', 'cookbooks' end context 'when cwd is in cookbooks/' do before { cwd 'cookbooks' } it "knife list -Rfp returns cookbooks" do knife('list -Rfp').should_succeed < (Chef::Platform.windows?) do before do directory 'real_cookbooks' symlink 'cookbooks', 'real_cookbooks' end context 'when cwd is in real_cookbooks/' do before { cwd 'real_cookbooks' } it "knife list -Rfp returns cookbooks" do knife('list -Rfp').should_succeed < false, :single_org => false do before do organization 'foo' end before :each do Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo') end context 'and is empty' do it "knife list / returns all top level directories" do knife('list /').should_succeed < false, :single_org => false do before do organization 'foo' end before :each do Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo') end it 'knife list -R / returns everything' do knife('list -R /').should_succeed <) # 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 'support/shared/integration/integration_helper' require 'chef/knife/list' require 'chef/knife/show' describe 'General chef_repo file system checks', :workstation do include IntegrationSupport include KnifeSupport context 'directories and files that should/should not be ignored' do when_the_repository "has empty roles, environments and data bag item directories" do before do directory "roles" directory "environments" directory "data_bags/bag1" end it "knife list --local -Rfp / returns them" do knife('list --local -Rfp /').should_succeed < "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n") /cookbooks/ EOM end end when_the_repository "has only empty cookbook subdirectories" do before { directory 'cookbooks/cookbook1/recipes' } it "knife list --local -Rfp / does not return it" do knife('list --local -Rfp /').should_succeed(< "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n") /cookbooks/ EOM end end when_the_repository "has empty and non-empty cookbook subdirectories" do before do directory 'cookbooks/cookbook1/recipes' file 'cookbooks/cookbook1/templates/default/x.txt', '' end it "knife list --local -Rfp / does not return the empty ones" do knife('list --local -Rfp /').should_succeed < "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n") /cookbooks/ EOM end end when_the_repository "has empty cookbook sub-sub-directories alongside non-empty ones" do before do file 'cookbooks/cookbook1/templates/default/x.txt', '' directory 'cookbooks/cookbook1/templates/rhel' directory 'cookbooks/cookbook1/files/default' end it "knife list --local -Rfp / does not return the empty ones" do knife('list --local -Rfp /').should_succeed <) # 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 'support/shared/integration/integration_helper' require 'chef/mixin/shell_out' describe "Knife cookbook API integration with IPv6", :workstation do include IntegrationSupport include Chef::Mixin::ShellOut when_the_chef_server "is bound to IPv6" do let(:chef_zero_opts) { {:host => "::1"} } let(:client_key) do <<-END_VALIDATION_PEM -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEApubutqtYYQ5UiA9QhWP7UvSmsfHsAoPKEVVPdVW/e8Svwpyf 0Xef6OFWVmBE+W442ZjLOe2y6p2nSnaq4y7dg99NFz6X+16mcKiCbj0RCiGqCvCk NftHhTgO9/RFvCbmKZ1RKNob1YzLrFpxBHaSh9po+DGWhApcd+I+op+ZzvDgXhNn 0nauZu3rZmApI/r7EEAOjFedAXs7VPNXhhtZAiLSAVIrwU3ZajtSzgXOxbNzgj5O AAAMmThK+71qPdffAdO4J198H6/MY04qgtFo7vumzCq0UCaGZfmeI1UNE4+xQWwP HJ3pDAP61C6Ebx2snI2kAd9QMx9Y78nIedRHPwIDAQABAoIBAHssRtPM1GacWsom 8zfeN6ZbI4KDlbetZz0vhnqDk9NVrpijWlcOP5dwZXVNitnB/HaqCqFvyPDY9JNB zI/pEFW4QH59FVDP42mVEt0keCTP/1wfiDDGh1vLqVBYl/ZphscDcNgDTzNkuxMx k+LFVxKnn3w7rGc59lALSkpeGvbbIDjp3LUMlUeCF8CIFyYZh9ZvXe4OCxYdyjxb i8tnMLKvJ4Psbh5jMapsu3rHQkfPdqzztQUz8vs0NYwP5vWge46FUyk+WNm/IhbJ G3YM22nwUS8Eu2bmTtADSJolATbCSkOwQ1D+Fybz/4obfYeGaCdOqB05ttubhenV ShsAb7ECgYEA20ecRVxw2S7qA7sqJ4NuYOg9TpfGooptYNA1IP971eB6SaGAelEL awYkGNuu2URmm5ElZpwJFFTDLGA7t2zB2xI1FeySPPIVPvJGSiZoFQOVlIg9WQzK 7jTtFQ/tOMrF+bigEUJh5bP1/7HzqSpuOsPjEUb2aoCTp+tpiRGL7TUCgYEAwtns g3ysrSEcTzpSv7fQRJRk1lkBhatgNd0oc+ikzf74DaVLhBg1jvSThDhiDCdB59mr Jh41cnR1XqE8jmdQbCDRiFrI1Pq6TPaDZFcovDVE1gue9x86v3FOH2ukPG4d2/Xy HevXjThtpMMsWFi0JYXuzXuV5HOvLZiP8sN3lSMCgYANpdxdGM7RRbE9ADY0dWK2 V14ReTLcxP7fyrWz0xLzEeCqmomzkz3BsIUoouu0DCTSw+rvAwExqcDoDylIVlWO fAifz7SeZHbcDxo+3TsXK7zwnLYsx7YNs2+aIv6hzUUbMNmNmXMcZ+IEwx+mRMTN lYmZdrA5mr0V83oDFPt/jQKBgC74RVE03pMlZiObFZNtheDiPKSG9Bz6wMh7NWMr c37MtZLkg52mEFMTlfPLe6ceV37CM8WOhqe+dwSGrYhOU06dYqUR7VOZ1Qr0aZvo fsNPu/Y0+u7rMkgv0fs1AXQnvz7kvKaF0YITVirfeXMafuKEtJoH7owRbur42cpV YCAtAoGAP1rHOc+w0RUcBK3sY7aErrih0OPh9U5bvJsrw1C0FIZhCEoDVA+fNIQL syHLXYFNy0OxMtH/bBAXBGNHd9gf5uOnqh0pYcbe/uRAxumC7Rl0cL509eURiA2T +vFmf54y9YdnLXaqv+FhJT6B6V7WX7IpU9BMqJY1cJYXHuHG2KA= -----END RSA PRIVATE KEY----- END_VALIDATION_PEM end let(:cache_path) do Dir.mktmpdir end let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") } let(:knife) { "ruby '#{chef_dir}/knife'" } let(:knife_config_flag) { "-c '#{path_to("config/knife.rb")}'" } # Some Solaris test platforms are too old for IPv6. These tests should not # otherwise be platform dependent, so exclude solaris context "and the chef_server_url contains an IPv6 literal", :not_supported_on_solaris do # This provides helper functions we need such as #path_to() when_the_repository "has the cookbook to be uploaded" do let(:knife_rb_content) do <<-END_CLIENT_RB chef_server_url "http://[::1]:8900" syntax_check_cache_path '#{cache_path}' client_key '#{path_to('config/knifeuser.pem')}' node_name 'whoisthisis' cookbook_path '#{CHEF_SPEC_DATA}/cookbooks' END_CLIENT_RB end before do file 'config/knife.rb', knife_rb_content file 'config/knifeuser.pem', client_key end it "successfully uploads a cookbook" do shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", :cwd => chef_dir) versions_list_json = Chef::HTTP::Simple.new("http://[::1]:8900").get("/cookbooks/apache2", "accept" => "application/json") versions_list = Chef::JSONCompat.from_json(versions_list_json) expect(versions_list["apache2"]["versions"]).not_to be_empty end context "and the cookbook has been uploaded to the server" do before do shell_out!("#{knife} cookbook upload apache2 #{knife_config_flag}", :cwd => chef_dir) end it "downloads the cookbook" do shell_out!("knife cookbook download apache2 #{knife_config_flag} -d #{cache_path}", :cwd => chef_dir) expect(Dir["#{cache_path}/*"].map {|entry| File.basename(entry)}).to include("apache2-0.0.1") end end end end end end chef-12.3.0/spec/integration/knife/delete_spec.rb0000644000004100000410000006545212520074675021730 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/delete' require 'chef/knife/list' require 'chef/knife/raw' describe 'knife delete', :workstation do include IntegrationSupport include KnifeSupport let :everything do < '{}' } environment 'x', '{}' node 'x', '{}' role 'x', '{}' user 'x', '{}' end when_the_repository 'also has one of each thing' do before do file 'clients/x.json', {} file 'cookbooks/x/metadata.rb', '' file 'data_bags/x/y.json', {} file 'environments/_default.json', {} file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/x.json', {} end it 'knife delete --both /cookbooks/x fails' do knife('delete --both /cookbooks/x').should_fail < "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", :stdout => "Deleted /environments/_default.json\n" knife('list -Rf /').should_succeed server_everything knife('list -Rf --local /').should_succeed < "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n" knife('list -Rf /').should_succeed server_everything knife('list -Rf --local /').should_succeed nothing end it 'knife delete --both / fails' do knife('delete --both /').should_fail "ERROR: / (remote) cannot be deleted.\nERROR: / (local) cannot be deleted.\n" knife('list -Rf /').should_succeed server_everything knife('list -Rf --local /').should_succeed nothing end it 'knife delete --both -r /* fails' do knife('delete --both -r /*').should_fail < /USAGE/ knife('list -Rf /').should_succeed < "ERROR: /environments/_default.json (remote) cannot be deleted (default environment cannot be modified).\n", :stdout => "Deleted /environments/_default.json\n" knife('list -Rf /').should_succeed server_nothing knife('list -Rf --local /').should_succeed < /USAGE/ knife('list -Rf /').should_succeed < '' } cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } end # TODO this seems wrong it 'knife delete --both -r /cookbooks/x deletes the latest version on the server and the local version' do knife('delete --both -r /cookbooks/x').should_succeed "Deleted /cookbooks/x\n" knife('raw /cookbooks/x').should_succeed(/1.0.0/) knife('list --local /cookbooks').should_succeed '' end end when_the_chef_server 'has an earlier version for the cookbook' do before do cookbook 'x', '1.0.0', { 'onlyin1.0.0.rb' => ''} cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } end it 'knife delete --both /cookbooks/x deletes the latest version on the server and the local version' do knife('delete --both -r /cookbooks/x').should_succeed "Deleted /cookbooks/x\n" knife('raw /cookbooks/x').should_succeed(/0.9.9/) knife('list --local /cookbooks').should_succeed '' end end when_the_chef_server 'has a later version for the cookbook, and no current version' do before { cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } } it 'knife delete --both /cookbooks/x deletes the server and client version of the cookbook' do knife('delete --both -r /cookbooks/x').should_succeed "Deleted /cookbooks/x\n" knife('raw /cookbooks/x').should_fail(/404/) knife('list --local /cookbooks').should_succeed '' end end when_the_chef_server 'has an earlier version for the cookbook, and no current version' do before { cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } } it 'knife delete --both /cookbooks/x deletes the server and client version of the cookbook' do knife('delete --both -r /cookbooks/x').should_succeed "Deleted /cookbooks/x\n" knife('raw /cookbooks/x').should_fail(/404/) knife('list --local /cookbooks').should_succeed '' end end end when_the_repository 'is empty' do when_the_chef_server 'has two versions of a cookbook' do before do cookbook 'x', '2.0.11' cookbook 'x', '11.0.0' end it 'knife delete deletes the latest version' do knife('delete --both -r /cookbooks/x').should_succeed "Deleted /cookbooks/x\n" knife('raw /cookbooks/x').should_succeed( /2.0.11/ ) end end end when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do before do organization 'foo' do container 'x', {} group 'x', {} end end before :each do Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo') end it 'knife delete /acls/containers/environments.json fails with a reasonable error' do knife('delete /acls/containers/environments.json').should_fail "ERROR: /acls/containers/environments.json (remote) cannot be deleted.\n" end it 'knife delete /containers/x.json succeeds' do knife('delete /containers/x.json').should_succeed "Deleted /containers/x.json\n" knife('raw /containers/x.json').should_fail(/404/) end it 'knife delete /groups/x.json succeeds' do knife('delete /groups/x.json').should_succeed "Deleted /groups/x.json\n" knife('raw /groups/x.json').should_fail(/404/) end it 'knife delete /org.json fails with a reasonable error' do knife('delete /org.json').should_fail "ERROR: /org.json (remote) cannot be deleted.\n" end it 'knife delete /invitations.json fails with a reasonable error' do knife('delete /invitations.json').should_fail "ERROR: /invitations.json (remote) cannot be deleted.\n" end it 'knife delete /members.json fails with a reasonable error' do knife('delete /members.json').should_fail "ERROR: /members.json (remote) cannot be deleted.\n" end end end chef-12.3.0/spec/integration/knife/upload_spec.rb0000644000004100000410000013474612520074675021755 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'chef/knife/upload' require 'chef/knife/diff' require 'chef/knife/raw' require 'chef/json_compat' describe 'knife upload', :workstation do include IntegrationSupport include KnifeSupport context 'without versioned cookbooks' do when_the_chef_server "has one of each thing" do before do client 'x', {} cookbook 'x', '1.0.0' data_bag 'x', { 'y' => {} } environment 'x', {} node 'x', {} role 'x', {} user 'x', {} end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife upload does nothing' do knife('upload /').should_succeed '' knife('diff --name-status /').should_succeed < "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n") Deleted extra entry /clients/chef-validator.json (purge is on) Deleted extra entry /clients/chef-webui.json (purge is on) Deleted extra entry /clients/x.json (purge is on) Deleted extra entry /cookbooks/x (purge is on) Deleted extra entry /data_bags/x (purge is on) Deleted extra entry /environments/x.json (purge is on) Deleted extra entry /nodes/x.json (purge is on) Deleted extra entry /roles/x.json (purge is on) Deleted extra entry /users/admin.json (purge is on) Deleted extra entry /users/x.json (purge is on) EOM knife('diff --name-status /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/_default.json', { "description" => "The default Chef environment" } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife upload makes no changes' do knife('upload /cookbooks/x').should_succeed '' knife('diff --name-status /').should_succeed '' end it 'knife upload --purge makes no changes' do knife('upload --purge /').should_succeed '' knife('diff --name-status /').should_succeed '' end context 'except the role file' do before do file 'roles/x.json', { 'description' => 'blarghle' } end it 'knife upload changes the role' do knife('upload /').should_succeed "Updated /roles/x.json\n" knife('diff --name-status /').should_succeed '' end it 'knife upload --no-diff does not change the role' do knife('upload --no-diff /').should_succeed '' knife('diff --name-status /').should_succeed "M\t/roles/x.json\n" end end context 'except the role file is textually different, but not ACTUALLY different' do before do file 'roles/x.json', < ChefZero::PUBLIC_KEY } file 'cookbooks/x/blah.rb', '' file 'cookbooks/y/metadata.rb', cb_metadata("y", "1.0.0") file 'data_bags/x/z.json', {} file 'data_bags/y/zz.json', {} file 'environments/y.json', {} file 'nodes/y.json', {} file 'roles/y.json', {} file 'users/y.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife upload adds the new files' do knife('upload /').should_succeed < /USAGE/ end end end end when_the_chef_server 'is empty' do when_the_repository 'has a data bag item' do before do file 'data_bags/x/y.json', { 'foo' => 'bar' } end it 'knife upload of the data bag uploads only the values in the data bag item and no other' do knife('upload /data_bags/x/y.json').should_succeed < false).keys.sort).to eq([ 'foo', 'id' ]) end it 'knife upload /data_bags/x /data_bags/x/y.json uploads x once' do knife('upload /data_bags/x /data_bags/x/y.json').should_succeed < 'aaa', 'data_bag' => 'bbb' } end it 'upload preserves chef_type and data_bag' do knife('upload /data_bags/x/y.json').should_succeed < false) expect(result.keys.sort).to eq([ 'chef_type', 'data_bag', 'id' ]) expect(result['chef_type']).to eq('aaa') expect(result['data_bag']).to eq('bbb') end end # Test upload of an item when the other end doesn't even have the container when_the_repository 'has two data bag items' do before do file 'data_bags/x/y.json', {} file 'data_bags/x/z.json', {} end it 'knife upload of one data bag item itself succeeds' do knife('upload /data_bags/x/y.json').should_succeed < {}, 'modified' => {}, 'unmodified' => {} } end when_the_repository 'has a modified, unmodified, added and deleted data bag item' do before do file 'data_bags/x/added.json', {} file 'data_bags/x/modified.json', { 'foo' => 'bar' } file 'data_bags/x/unmodified.json', {} end it 'knife upload of the modified file succeeds' do knife('upload /data_bags/x/modified.json').should_succeed < /USAGE/ end it 'knife upload --purge . uploads everything' do knife('upload --purge .').should_succeed < '' } end when_the_repository 'has a modified, extra and missing file for the cookbook' do before do file 'cookbooks/x/metadata.rb', cb_metadata("x", "1.0.0", "#modified") file 'cookbooks/x/y.rb', 'hi' end it 'knife upload of any individual file fails' do knife('upload /cookbooks/x/metadata.rb').should_fail "ERROR: /cookbooks/x/metadata.rb cannot be updated.\n" knife('upload /cookbooks/x/y.rb').should_fail "ERROR: /cookbooks/x cannot have a child created under it.\n" knife('upload --purge /cookbooks/x/z.rb').should_fail "ERROR: /cookbooks/x/z.rb cannot be deleted.\n" end # TODO this is a bit of an inconsistency: if we didn't specify --purge, # technically we shouldn't have deleted missing files. But ... cookbooks # are a special case. it 'knife upload of the cookbook itself succeeds' do knife('upload /cookbooks/x').should_succeed < true end when_the_repository 'has an update to said cookbook' do before do file 'cookbooks/frozencook/metadata.rb', cb_metadata("frozencook", "1.0.0", "# This is different") end it 'knife upload fails to upload the frozen cookbook' do knife('upload /cookbooks/frozencook').should_fail "ERROR: /cookbooks failed to write: Cookbook frozencook is frozen\n" end it 'knife upload --force uploads the frozen cookbook' do knife('upload --force /cookbooks/frozencook').should_succeed < '' } cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } end it 'knife upload /cookbooks/x uploads the local version' do knife('diff --name-status /cookbooks').should_succeed < ''} cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } end it 'knife upload /cookbooks/x uploads the local version' do knife('upload --purge /cookbooks/x').should_succeed < 'hi' } end it 'knife upload /cookbooks/x uploads the local version' do knife('diff --name-status /cookbooks').should_succeed < 'hi' } end it 'knife upload /cookbooks/x uploads the new version' do knife('upload --purge /cookbooks/x').should_succeed < warn) end end when_the_repository 'has the same environment with the wrong name in the file' do before do file 'environments/x.json', { 'name' => 'y' } end it 'knife upload fails' do knife('upload /environments/x.json').should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n" knife('diff --name-status /environments/x.json').should_succeed "M\t/environments/x.json\n" end end when_the_repository 'has the same environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife upload succeeds' do knife('upload /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end end when_the_chef_server 'is empty' do when_the_repository 'has an environment with the wrong name in the file' do before do file 'environments/x.json', { 'name' => 'y' } end it 'knife upload fails' do knife('upload /environments/x.json').should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n" knife('diff --name-status /environments/x.json').should_succeed "A\t/environments/x.json\n" end end when_the_repository 'has an environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife upload succeeds' do knife('upload /environments/x.json').should_succeed "Created /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end when_the_repository 'has a data bag with no id in the file' do before do file 'data_bags/bag/x.json', { 'foo' => 'bar' } end it 'knife upload succeeds' do knife('upload /data_bags/bag/x.json').should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n" knife('diff --name-status /data_bags/bag/x.json').should_succeed '' end end end end # without versioned cookbooks with_versioned_cookbooks do when_the_chef_server "has one of each thing" do before do client 'x', {} cookbook 'x', '1.0.0' data_bag 'x', { 'y' => {} } environment 'x', {} node 'x', {} role 'x', {} user 'x', {} end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife upload does nothing' do knife('upload /').should_succeed '' knife('diff --name-status /').should_succeed < "WARNING: /environments/_default.json cannot be deleted (default environment cannot be modified).\n") Deleted extra entry /clients/chef-validator.json (purge is on) Deleted extra entry /clients/chef-webui.json (purge is on) Deleted extra entry /clients/x.json (purge is on) Deleted extra entry /cookbooks/x-1.0.0 (purge is on) Deleted extra entry /data_bags/x (purge is on) Deleted extra entry /environments/x.json (purge is on) Deleted extra entry /nodes/x.json (purge is on) Deleted extra entry /roles/x.json (purge is on) Deleted extra entry /users/admin.json (purge is on) Deleted extra entry /users/x.json (purge is on) EOM knife('diff --name-status /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x-1.0.0/metadata.rb', cb_metadata('x', '1.0.0') file 'data_bags/x/y.json', {} file 'environments/_default.json', { 'description' => 'The default Chef environment' } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife upload makes no changes' do knife('upload /cookbooks/x-1.0.0').should_succeed '' knife('diff --name-status /').should_succeed '' end it 'knife upload --purge makes no changes' do knife('upload --purge /').should_succeed '' knife('diff --name-status /').should_succeed '' end context 'except the role file' do before do file 'roles/x.json', { 'description' => 'blarghle' } end it 'knife upload changes the role' do knife('upload /').should_succeed "Updated /roles/x.json\n" knife('diff --name-status /').should_succeed '' end end context 'except the role file is textually different, but not ACTUALLY different' do before do file 'roles/x.json', < ChefZero::PUBLIC_KEY } file 'cookbooks/x-1.0.0/blah.rb', '' file 'cookbooks/x-2.0.0/metadata.rb', cb_metadata('x', '2.0.0') file 'cookbooks/y-1.0.0/metadata.rb', cb_metadata('y', '1.0.0') file 'data_bags/x/z.json', {} file 'data_bags/y/zz.json', {} file 'environments/y.json', {} file 'nodes/y.json', {} file 'roles/y.json', {} file 'users/y.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife upload adds the new files' do knife('upload /').should_succeed < /USAGE/ end end end end # Test upload of an item when the other end doesn't even have the container when_the_chef_server 'is empty' do when_the_repository 'has two data bag items' do before do file 'data_bags/x/y.json', {} file 'data_bags/x/z.json', {} end it 'knife upload of one data bag item itself succeeds' do knife('upload /data_bags/x/y.json').should_succeed < {}, 'modified' => {}, 'unmodified' => {} } end when_the_repository 'has a modified, unmodified, added and deleted data bag item' do before do file 'data_bags/x/added.json', {} file 'data_bags/x/modified.json', { 'foo' => 'bar' } file 'data_bags/x/unmodified.json', {} end it 'knife upload of the modified file succeeds' do knife('upload /data_bags/x/modified.json').should_succeed < /USAGE/ end it 'knife upload --purge . uploads everything' do knife('upload --purge .').should_succeed < '' } end when_the_repository 'has a modified, extra and missing file for the cookbook' do before do file 'cookbooks/x-1.0.0/metadata.rb', cb_metadata('x', '1.0.0', '#modified') file 'cookbooks/x-1.0.0/y.rb', 'hi' end it 'knife upload of any individual file fails' do knife('upload /cookbooks/x-1.0.0/metadata.rb').should_fail "ERROR: /cookbooks/x-1.0.0/metadata.rb cannot be updated.\n" knife('upload /cookbooks/x-1.0.0/y.rb').should_fail "ERROR: /cookbooks/x-1.0.0 cannot have a child created under it.\n" knife('upload --purge /cookbooks/x-1.0.0/z.rb').should_fail "ERROR: /cookbooks/x-1.0.0/z.rb cannot be deleted.\n" end # TODO this is a bit of an inconsistency: if we didn't specify --purge, # technically we shouldn't have deleted missing files. But ... cookbooks # are a special case. it 'knife upload of the cookbook itself succeeds' do knife('upload /cookbooks/x-1.0.0').should_succeed < '' } cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } end it 'knife upload /cookbooks uploads the local version' do knife('diff --name-status /cookbooks').should_succeed < ''} cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } end it 'knife upload /cookbooks uploads the local version' do knife('upload --purge /cookbooks').should_succeed < 'hi' } end it 'knife upload /cookbooks/x uploads the local version' do knife('diff --name-status /cookbooks').should_succeed < 'hi' } end it 'knife upload /cookbooks/x uploads the new version' do knife('upload --purge /cookbooks').should_succeed < 'y' } end it 'knife upload fails' do knife('upload /environments/x.json').should_fail "ERROR: /environments/x.json failed to write: Name must be 'x' (is 'y')\n" knife('diff --name-status /environments/x.json').should_succeed "M\t/environments/x.json\n" end end when_the_repository 'has the same environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife upload succeeds' do knife('upload /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end end when_the_chef_server 'is empty' do when_the_repository 'has an environment with the wrong name in the file' do before do file 'environments/x.json', { 'name' => 'y' } end it 'knife upload fails' do knife('upload /environments/x.json').should_fail "ERROR: /environments failed to create_child: Error creating 'x.json': Name must be 'x' (is 'y')\n" knife('diff --name-status /environments/x.json').should_succeed "A\t/environments/x.json\n" end end when_the_repository 'has an environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife upload succeeds' do knife('upload /environments/x.json').should_succeed "Created /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end when_the_repository 'has a data bag with no id in the file' do before do file 'data_bags/bag/x.json', { 'foo' => 'bar' } end it 'knife upload succeeds' do knife('upload /data_bags/bag/x.json').should_succeed "Created /data_bags/bag\nCreated /data_bags/bag/x.json\n" knife('diff --name-status /data_bags/bag/x.json').should_succeed '' end end end end # with versioned cookbooks when_the_chef_server 'has a user' do before do user 'x', {} end when_the_repository 'has the same user with json_class in it' do before do file 'users/x.json', { 'admin' => true, 'json_class' => 'Chef::WebUIUser' } end it 'knife upload /users/x.json succeeds' do knife('upload /users/x.json').should_succeed "Updated /users/x.json\n" end end end when_the_chef_server "is in Enterprise mode", :osc_compat => false, :single_org => false do before do user 'foo', {} user 'bar', {} user 'foobar', {} organization 'foo', { 'full_name' => 'Something'} end before :each do Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo') end context 'and has nothing but a single group named blah' do group 'blah', {} when_the_repository 'has one of each thing' do before do # TODO We have to upload acls for an existing group due to a lack of # dependency detection during upload. Fix that! file 'acls/groups/blah.json', {} file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'containers/x.json', {} file 'cookbooks/x/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/x.json', {} file 'groups/x.json', {} file 'invitations.json', [ 'foo' ] file 'members.json', [ 'bar' ] file 'nodes/x.json', {} file 'org.json', { 'full_name' => 'wootles' } file 'roles/x.json', {} end it 'knife upload / uploads everything' do knife('upload /').should_succeed < 'Something' } end it 'knife upload / emits a warning for bar and adds foo and foobar' do knife('upload /').should_succeed '' expect(api.get('/')['full_name']).to eq('Something') end end when_the_repository 'has an org.json that changes full_name' do before do file 'org.json', { 'full_name' => 'Something Else'} end it 'knife upload / emits a warning for bar and adds foo and foobar' do knife('upload /').should_succeed "Updated /org.json\n" expect(api.get('/')['full_name']).to eq('Something Else') end end context 'and has invited foo and bar is already a member' do org_invite 'foo' org_member 'bar' when_the_repository 'wants to invite foo, bar and foobar' do before do file 'invitations.json', [ 'foo', 'bar', 'foobar' ] end it 'knife upload / emits a warning for bar and invites foobar' do knife('upload /').should_succeed "Updated /invitations.json\n", :stderr => "WARN: Could not invite bar to organization foo: User bar is already in organization foo\n" expect(api.get('association_requests').map { |a| a['username'] }).to eq([ 'foo', 'foobar' ]) expect(api.get('users').map { |a| a['user']['username'] }).to eq([ 'bar' ]) end end when_the_repository 'wants to make foo, bar and foobar members' do before do file 'members.json', [ 'foo', 'bar', 'foobar' ] end it 'knife upload / emits a warning for bar and adds foo and foobar' do knife('upload /').should_succeed "Updated /members.json\n" expect(api.get('association_requests').map { |a| a['username'] }).to eq([ ]) expect(api.get('users').map { |a| a['user']['username'] }).to eq([ 'bar', 'foo', 'foobar' ]) end end when_the_repository 'wants to invite foo and have bar as a member' do before do file 'invitations.json', [ 'foo' ] file 'members.json', [ 'bar' ] end it 'knife upload / does nothing' do knife('upload /').should_succeed '' expect(api.get('association_requests').map { |a| a['username'] }).to eq([ 'foo' ]) expect(api.get('users').map { |a| a['user']['username'] }).to eq([ 'bar' ]) end end end context 'and has invited bar and foo' do org_invite 'bar', 'foo' when_the_repository 'wants to invite foo and bar (different order)' do before do file 'invitations.json', [ 'foo', 'bar' ] end it 'knife upload / does nothing' do knife('upload /').should_succeed '' expect(api.get('association_requests').map { |a| a['username'] }).to eq([ 'bar', 'foo' ]) expect(api.get('users').map { |a| a['user']['username'] }).to eq([ ]) end end end context 'and has already added bar and foo as members of the org' do org_member 'bar', 'foo' when_the_repository 'wants to add foo and bar (different order)' do before do file 'members.json', [ 'foo', 'bar' ] end it 'knife upload / does nothing' do knife('upload /').should_succeed '' expect(api.get('association_requests').map { |a| a['username'] }).to eq([ ]) expect(api.get('users').map { |a| a['user']['username'] }).to eq([ 'bar', 'foo' ]) end end end end end end chef-12.3.0/spec/integration/knife/raw_spec.rb0000644000004100000410000001225012520074675021243 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/raw' require 'chef/knife/show' describe 'knife raw', :workstation do include IntegrationSupport include KnifeSupport include AppServerSupport include_context "default config options" when_the_chef_server "has one of each thing" do before do client 'x', '{}' cookbook 'x', '1.0.0' data_bag 'x', { 'y' => '{}' } environment 'x', '{}' node 'x', '{}' role 'x', '{}' user 'x', '{}' end it 'knife raw /nodes/x returns the node', :skip => (RUBY_VERSION < "1.9") do knife('raw /nodes/x').should_succeed < (RUBY_VERSION < "1.9") do knife('raw -m DELETE /roles/x').should_succeed < (RUBY_VERSION < "1.9") do Tempfile.open('raw_put_input') do |file| file.write < (RUBY_VERSION < "1.9") do Tempfile.open('raw_put_input') do |file| file.write < 'application/json' }, ['{ "x": "y", "a": "b" }'] ] end @raw_server, @raw_server_thread = start_app_server(app, 9018) end after :each do @raw_server.shutdown if @raw_server @raw_server_thread.kill if @raw_server_thread end it 'knife raw /blah returns the prettified json', :skip => (RUBY_VERSION < "1.9") do knife('raw /blah').should_succeed < 'text' }, ['{ "x": "y", "a": "b" }'] ] end @raw_server, @raw_server_thread = start_app_server(app, 9018) end after :each do @raw_server.shutdown if @raw_server @raw_server_thread.kill if @raw_server_thread end it 'knife raw /blah returns the raw text' do knife('raw /blah').should_succeed(<) # 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 'support/shared/integration/integration_helper' require 'chef/knife/list' require 'chef/knife/show' describe 'chefignore tests', :workstation do include IntegrationSupport include KnifeSupport when_the_repository "has lots of stuff in it" do before do file 'roles/x.json', {} file 'environments/x.json', {} file 'data_bags/bag1/x.json', {} file 'cookbooks/cookbook1/x.json', {} end context "and has a chefignore everywhere except cookbooks" do before do chefignore = "x.json\nroles/x.json\nenvironments/x.json\ndata_bags/bag1/x.json\nbag1/x.json\ncookbooks/cookbook1/x.json\ncookbook1/x.json\n" file 'chefignore', chefignore file 'roles/chefignore', chefignore file 'environments/chefignore', chefignore file 'data_bags/chefignore', chefignore file 'data_bags/bag1/chefignore', chefignore file 'cookbooks/cookbook1/chefignore', chefignore end it 'matching files and directories get ignored' do # NOTE: many of the "chefignore" files should probably not show up # themselves, but we have other tests that talk about that knife('list --local -Rfp /').should_succeed < "WARN: Cookbook 'cookbook1' is empty or entirely chefignored at #{Chef::Config.chef_repo_path}/cookbooks/cookbook1\n") /cookbooks/ EOM end end when_the_repository "has multiple cookbooks" do before do file 'cookbooks/cookbook1/x.json', {} file 'cookbooks/cookbook1/y.json', {} file 'cookbooks/cookbook2/x.json', {} file 'cookbooks/cookbook2/y.json', {} end context 'and has a chefignore with filenames' do before { file 'cookbooks/chefignore', "x.json\n" } it 'matching files and directories get ignored in all cookbooks' do knife('list --local -Rfp /').should_succeed < "WARN: Child with name 'yourcookbook' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks1/yourcookbook and #{Chef::Config.chef_repo_path}/cookbooks2/yourcookbook\n") /cookbooks/ /cookbooks/mycookbook/ /cookbooks/mycookbook/x.json /cookbooks/yourcookbook/ /cookbooks/yourcookbook/onlyincookbooks1.rb /cookbooks/yourcookbook/x.json EOM end end end end when_the_repository 'has a cookbook named chefignore' do before do file 'cookbooks/chefignore/metadata.rb', {} end it 'knife list -Rfp /cookbooks shows it' do knife('list --local -Rfp /cookbooks').should_succeed <) # 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 'support/shared/integration/integration_helper' require 'chef/knife/download' require 'chef/knife/diff' describe 'knife download', :workstation do include IntegrationSupport include KnifeSupport context 'without versioned cookbooks' do when_the_chef_server "has one of each thing" do before do client 'x', {} cookbook 'x', '1.0.0' data_bag 'x', { 'y' => {} } environment 'x', {} node 'x', {} role 'x', {} user 'x', {} end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife download downloads everything' do knife('download /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/_default.json', { "description" => "The default Chef environment" } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife download makes no changes' do knife('download /').should_succeed '' knife('diff --name-status /').should_succeed '' end it 'knife download --purge makes no changes' do knife('download --purge /').should_succeed '' knife('diff --name-status /').should_succeed '' end context 'except the role file' do before do file 'roles/x.json', < ChefZero::PUBLIC_KEY } file 'cookbooks/x/blah.rb', '' file 'cookbooks/y/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/z.json', {} file 'data_bags/y/zz.json', {} file 'environments/y.json', {} file 'nodes/y.json', {} file 'roles/y.json', {} file 'users/y.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife download does nothing' do knife('download /').should_succeed '' knife('diff --name-status /').should_succeed < /USAGE/ end end end end # Test download of an item when the other end doesn't even have the container when_the_repository 'is empty' do when_the_chef_server 'has two data bag items' do before do data_bag 'x', { 'y' => {}, 'z' => {} } end it 'knife download of one data bag item itself succeeds' do knife('download /data_bags/x/y.json').should_succeed < {}, 'modified' => { 'foo' => 'bar' }, 'unmodified' => {} } end it 'knife download of the modified file succeeds' do knife('download /data_bags/x/modified.json').should_succeed < /USAGE/ end it 'knife download --purge . downloads everything' do knife('download --purge .').should_succeed < cb_metadata("x", "1.0.0", "#extra content"), 'y.rb' => 'hi' } end it 'knife download of a modified file succeeds' do knife('download /cookbooks/x/metadata.rb').should_succeed "Updated /cookbooks/x/metadata.rb\n" knife('diff --name-status /cookbooks').should_succeed < '' } cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } end it 'knife download /cookbooks/x downloads the latest version' do knife('download --purge /cookbooks/x').should_succeed < ''} cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } end it 'knife download /cookbooks/x downloads the updated file' do knife('download --purge /cookbooks/x').should_succeed < 'hi' } end it 'knife download /cookbooks/x downloads the latest version' do knife('download --purge /cookbooks/x').should_succeed < 'hi' } end it 'knife download /cookbooks/x downloads the old version' do knife('download --purge /cookbooks/x').should_succeed < warning knife('diff --name-status /environments/x.json').should_succeed '' end end when_the_repository 'has the same environment with the wrong name in the file' do before do file 'environments/x.json', { 'name' => 'y' } end it 'knife download succeeds' do knife('download /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end when_the_repository 'has the same environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife download succeeds' do knife('download /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end end end # without versioned cookbooks with_versioned_cookbooks do when_the_chef_server "has one of each thing" do before do client 'x', {} cookbook 'x', '1.0.0' data_bag 'x', { 'y' => {} } environment 'x', {} node 'x', {} role 'x', {} user 'x', {} end when_the_repository 'has only top-level directories' do before do directory 'clients' directory 'cookbooks' directory 'data_bags' directory 'environments' directory 'nodes' directory 'roles' directory 'users' end it 'knife download downloads everything' do knife('download /').should_succeed < true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/chef-webui.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'clients/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } file 'cookbooks/x-1.0.0/metadata.rb', cb_metadata("x", "1.0.0") file 'data_bags/x/y.json', {} file 'environments/_default.json', { "description" => "The default Chef environment" } file 'environments/x.json', {} file 'nodes/x.json', {} file 'roles/x.json', {} file 'users/admin.json', { 'admin' => true, 'public_key' => ChefZero::PUBLIC_KEY } file 'users/x.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife download makes no changes' do knife('download /').should_succeed '' knife('diff --name-status /').should_succeed '' end it 'knife download --purge makes no changes' do knife('download --purge /').should_succeed '' knife('diff --name-status /').should_succeed '' end context 'except the role file' do before do file 'roles/x.json', { "description" => "blarghle" } end it 'knife download changes the role' do knife('download /').should_succeed "Updated /roles/x.json\n" knife('diff --name-status /').should_succeed '' end end context 'except the role file is textually different, but not ACTUALLY different' do before do file 'roles/x.json', < ChefZero::PUBLIC_KEY } file 'cookbooks/x-1.0.0/blah.rb', '' file 'cookbooks/x-2.0.0/metadata.rb', 'version "2.0.0"' file 'cookbooks/y-1.0.0/metadata.rb', 'version "1.0.0"' file 'data_bags/x/z.json', {} file 'data_bags/y/zz.json', {} file 'environments/y.json', {} file 'nodes/y.json', {} file 'roles/y.json', {} file 'users/y.json', { 'public_key' => ChefZero::PUBLIC_KEY } end it 'knife download does nothing' do knife('download /').should_succeed '' knife('diff --name-status /').should_succeed < /USAGE/ end end end end # Test download of an item when the other end doesn't even have the container when_the_repository 'is empty' do when_the_chef_server 'has two data bag items' do before do data_bag 'x', { 'y' => {}, 'z' => {} } end it 'knife download of one data bag item itself succeeds' do knife('download /data_bags/x/y.json').should_succeed < {}, 'modified' => { 'foo' => 'bar' }, 'unmodified' => {} } end it 'knife download of the modified file succeeds' do knife('download /data_bags/x/modified.json').should_succeed < /USAGE/ end it 'knife download --purge . downloads everything' do knife('download --purge .').should_succeed < 'hi' } end it 'knife download of a modified file succeeds' do knife('download /cookbooks/x-1.0.0/metadata.rb').should_succeed "Updated /cookbooks/x-1.0.0/metadata.rb\n" knife('diff --name-status /cookbooks').should_succeed < '' } cookbook 'x', '1.0.1', { 'onlyin1.0.1.rb' => 'hi' } end it 'knife download /cookbooks/x downloads the latest version' do knife('download --purge /cookbooks').should_succeed < ''} cookbook 'x', '0.9.9', { 'onlyin0.9.9.rb' => 'hi' } end it 'knife download /cookbooks downloads the updated file' do knife('download --purge /cookbooks').should_succeed < 'hi' } end it 'knife download /cookbooks/x downloads the latest version' do knife('download --purge /cookbooks').should_succeed < 'hi' } end it 'knife download --purge /cookbooks downloads the old version and deletes the new version' do knife('download --purge /cookbooks').should_succeed < 'y' } end it 'knife download succeeds' do knife('download /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end when_the_repository 'has the same environment with no name in the file' do before do file 'environments/x.json', { 'description' => 'hi' } end it 'knife download succeeds' do knife('download /environments/x.json').should_succeed "Updated /environments/x.json\n" knife('diff --name-status /environments/x.json').should_succeed '' end end end end # with versioned cookbooks when_the_chef_server 'has a cookbook' do before do cookbook 'x', '1.0.0' end when_the_repository 'is empty' do it 'knife download /cookbooks/x signs all requests' do # Check that BasicClient.request() always gets called with X-OPS-USERID original_new = Chef::HTTP::BasicClient.method(:new) expect(Chef::HTTP::BasicClient).to receive(:new) { |args| new_result = original_new.call(*args) original_request = new_result.method(:request) expect(new_result).to receive(:request) { |method, url, body, headers, &response_handler| expect(headers['X-OPS-USERID']).not_to be_nil original_request.call(method, url, body, headers, &response_handler) }.at_least(:once) new_result }.at_least(:once) knife('download /cookbooks/x').should_succeed < false, :single_org => false do before do organization 'foo' do container 'x', {} group 'x', {} end end before :each do Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo') end when_the_repository 'is empty' do it 'knife download / downloads everything' do knife('download /').should_succeed <) # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/list' describe 'redirection', :workstation do include IntegrationSupport include KnifeSupport include AppServerSupport include_context "default config options" when_the_chef_server 'has a role' do before { role 'x', {} } context 'and another server redirects to it with 302' do before :each do real_chef_server_url = Chef::Config.chef_server_url Chef::Config.chef_server_url = "http://localhost:9018" app = lambda do |env| [302, {'Content-Type' => 'text','Location' => "#{real_chef_server_url}#{env['PATH_INFO']}" }, ['302 found'] ] end @redirector_server, @redirector_server_thread = start_app_server(app, 9018) end after :each do @redirector_server.shutdown if @redirector_server @redirector_thread.kill if @redirector_thread end it 'knife list /roles returns the role' do knife('list /roles').should_succeed "/roles/x.json\n" end end end end chef-12.3.0/spec/integration/knife/show_spec.rb0000644000004100000410000001175612520074675021444 0ustar www-datawww-data# # Author:: John Keiser () # 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 'support/shared/integration/integration_helper' require 'support/shared/context/config' require 'chef/knife/show' describe 'knife show', :workstation do include IntegrationSupport include KnifeSupport include_context "default config options" when_the_chef_server "has one of each thing" do before do client 'x', '{}' cookbook 'x', '1.0.0' data_bag 'x', { 'y' => '{}' } environment 'x', '{}' node 'x', '{}' role 'x', '{}' user 'x', '{}' end when_the_repository 'also has one of each thing' do before do file 'clients/x.json', { 'foo' => 'bar' } file 'cookbooks/x/metadata.rb', cb_metadata('x', '1.0.0') file 'data_bags/x/y.json', { 'foo' => 'bar' } file 'environments/_default.json', { 'foo' => 'bar' } file 'environments/x.json', { 'foo' => 'bar' } file 'nodes/x.json', { 'foo' => 'bar' } file 'roles/x.json', { 'foo' => 'bar' } file 'users/x.json', { 'foo' => 'bar' } end it 'knife show /cookbooks/x/metadata.rb shows the remote version' do knife('show /cookbooks/x/metadata.rb').should_succeed < (RUBY_VERSION < "1.9") do knife('show /environments/x.json').should_succeed < (RUBY_VERSION < "1.9") do knife('show /roles/x.json').should_succeed < { 'foo' => 'bar' }, 'cookbook_versions' => { 'blah' => '= 1.0.0'}, 'override_attributes' => { 'x' => 'y' }, 'description' => 'woo', 'name' => 'x' } end it 'knife show shows the attributes in a predetermined order', :skip => (RUBY_VERSION < "1.9") do knife('show /environments/x.json').should_succeed < chef_dir) result.error! expect(result.stdout).to include("ITWORKS") end it "should evaluate its node.json file" do file 'config/solo.rb', < chef_dir) result.error! expect(result.stdout).to include("ITWORKS") end end when_the_repository "has a cookbook with an undeclared dependency" do before do file 'cookbooks/x/metadata.rb', cookbook_x_100_metadata_rb file 'cookbooks/x/recipes/default.rb', 'include_recipe "ancient::aliens"' file 'cookbooks/ancient/metadata.rb', cookbook_ancient_100_metadata_rb file 'cookbooks/ancient/recipes/aliens.rb', 'print "it was aliens"' end it "should exit with an error" do file 'config/solo.rb', < chef_dir) expect(result.exitstatus).to eq(0) # For CHEF-5120 this becomes 1 expect(result.stdout).to include("WARN: MissingCookbookDependency") end end when_the_repository "has a cookbook with a recipe with sleep" do before do directory 'logs' file 'logs/runs.log', '' file 'cookbooks/x/metadata.rb', cookbook_x_100_metadata_rb file 'cookbooks/x/recipes/default.rb', < chef_dir) # Give it some time to progress sleep 1 # Instantiate the second chef-solo run s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \ -l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir) Process.waitpid(s1) Process.waitpid(s2) end }.not_to raise_error # Unfortunately file / directory helpers in integration tests # are implemented using before(:each) so we need to do all below # checks in one example. run_log = File.read(path_to('logs/runs.log')) # both of the runs should succeed expect(run_log.lines.reject {|l| !l.include? "INFO: Chef Run complete in"}.length).to eq(2) # second run should have a message which indicates it's waiting for the first run pid_lines = run_log.lines.reject {|l| !l.include? "Chef-client pid:"} expect(pid_lines.length).to eq(2) pids = pid_lines.map {|l| l.split(" ").last} expect(run_log).to include("Chef client #{pids[0]} is running, will wait for it to finish and then run.") # second run should start after first run ends starts = [ ] ends = [ ] run_log.lines.each_with_index do |line, index| if line.include? "Chef-client pid:" starts << index elsif line.include? "INFO: Chef Run complete in" ends << index end end expect(starts[1]).to be > ends[0] end end end chef-12.3.0/spec/integration/recipes/0000755000004100000410000000000012520074675017451 5ustar www-datawww-datachef-12.3.0/spec/integration/recipes/lwrp_inline_resources_spec.rb0000644000004100000410000000446712520074675025437 0ustar www-datawww-datarequire 'support/shared/integration/integration_helper' require 'chef/mixin/shell_out' describe "LWRPs with inline resources" do include IntegrationSupport include Chef::Mixin::ShellOut let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") } # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the # following constraints are satisfied: # * Windows: windows can only run batch scripts as bare executables. Rubygems # creates batch wrappers for installed gems, but we don't have batch wrappers # in the source tree. # * Other `chef-client` in PATH: A common case is running the tests on a # machine that has omnibus chef installed. In that case we need to ensure # we're running `chef-client` from the source tree and not the external one. # cf. CHEF-4914 let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" } when_the_repository "has a cookbook with a nested LWRP" do before do directory 'cookbooks/x' do file 'resources/do_nothing.rb', < chef_dir) actual = result.stdout.lines.map { |l| l.chomp }.join("\n") expected = <) # 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' class TinyClass include Chef::Mixin::ParamsValidate def music(is_good=true) is_good end end describe Chef::Mixin::ParamsValidate do before(:each) do @vo = TinyClass.new() end it "should allow a hash and a hash as arguments to validate" do expect { @vo.validate({:one => "two"}, {}) }.not_to raise_error end it "should raise an argument error if validate is called incorrectly" do expect { @vo.validate("one", "two") }.to raise_error(ArgumentError) end it "should require validation map keys to be symbols or strings" do expect { @vo.validate({:one => "two"}, { :one => true }) }.not_to raise_error expect { @vo.validate({:one => "two"}, { "one" => true }) }.not_to raise_error expect { @vo.validate({:one => "two"}, { Hash.new => true }) }.to raise_error(ArgumentError) end it "should allow options to be required with true" do expect { @vo.validate({:one => "two"}, { :one => true }) }.not_to raise_error end it "should allow options to be optional with false" do expect { @vo.validate({}, {:one => false})}.not_to raise_error end it "should allow you to check what kind_of? thing an argument is with kind_of" do expect { @vo.validate( {:one => "string"}, { :one => { :kind_of => String } } ) }.not_to raise_error expect { @vo.validate( {:one => "string"}, { :one => { :kind_of => Array } } ) }.to raise_error(ArgumentError) end it "should allow you to specify an argument is required with required" do expect { @vo.validate( {:one => "string"}, { :one => { :required => true } } ) }.not_to raise_error expect { @vo.validate( {:two => "string"}, { :one => { :required => true } } ) }.to raise_error(ArgumentError) expect { @vo.validate( {:two => "string"}, { :one => { :required => false } } ) }.not_to raise_error end it "should allow you to specify whether an object has a method with respond_to" do expect { @vo.validate( {:one => @vo}, { :one => { :respond_to => "validate" } } ) }.not_to raise_error expect { @vo.validate( {:one => @vo}, { :one => { :respond_to => "monkey" } } ) }.to raise_error(ArgumentError) end it "should allow you to specify whether an object has all the given methods with respond_to and an array" do expect { @vo.validate( {:one => @vo}, { :one => { :respond_to => ["validate", "music"] } } ) }.not_to raise_error expect { @vo.validate( {:one => @vo}, { :one => { :respond_to => ["monkey", "validate"] } } ) }.to raise_error(ArgumentError) end it "should let you set a default value with default => value" do arguments = Hash.new @vo.validate(arguments, { :one => { :default => "is the loneliest number" } }) expect(arguments[:one]).to eq("is the loneliest number") end it "should let you check regular expressions" do expect { @vo.validate( { :one => "is good" }, { :one => { :regex => /^is good$/ } } ) }.not_to raise_error expect { @vo.validate( { :one => "is good" }, { :one => { :regex => /^is bad$/ } } ) }.to raise_error(ArgumentError) end it "should let you specify your own callbacks" do expect { @vo.validate( { :one => "is good" }, { :one => { :callbacks => { "should be equal to is good" => lambda { |a| a == "is good" }, } } } ) }.not_to raise_error expect { @vo.validate( { :one => "is bad" }, { :one => { :callbacks => { "should be equal to 'is good'" => lambda { |a| a == "is good" }, } } } ) }.to raise_error(ArgumentError) end it "should let you combine checks" do args = { :one => "is good", :two => "is bad" } expect { @vo.validate( args, { :one => { :kind_of => String, :respond_to => [ :to_s, :upcase ], :regex => /^is good/, :callbacks => { "should be your friend" => lambda { |a| a == "is good" } }, :required => true }, :two => { :kind_of => String, :required => false }, :three => { :default => "neato mosquito" } } ) }.not_to raise_error expect(args[:three]).to eq("neato mosquito") expect { @vo.validate( args, { :one => { :kind_of => String, :respond_to => [ :to_s, :upcase ], :regex => /^is good/, :callbacks => { "should be your friend" => lambda { |a| a == "is good" } }, :required => true }, :two => { :kind_of => Hash, :required => false }, :three => { :default => "neato mosquito" } } ) }.to raise_error(ArgumentError) end it "should raise an ArgumentError if the validation map has an unknown check" do expect { @vo.validate( { :one => "two" }, { :one => { :busted => "check" } } ) }.to raise_error(ArgumentError) end it "should accept keys that are strings in the options" do expect { @vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ }}) }.not_to raise_error end it "should allow an array to kind_of" do expect { @vo.validate( {:one => "string"}, { :one => { :kind_of => [ String, Array ] } } ) }.not_to raise_error expect { @vo.validate( {:one => ["string"]}, { :one => { :kind_of => [ String, Array ] } } ) }.not_to raise_error expect { @vo.validate( {:one => Hash.new}, { :one => { :kind_of => [ String, Array ] } } ) }.to raise_error(ArgumentError) end it "asserts that a value returns false from a predicate method" do expect do @vo.validate({:not_blank => "should pass"}, {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}}) end.not_to raise_error expect do @vo.validate({:not_blank => ""}, {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}}) end.to raise_error(Chef::Exceptions::ValidationFailed) end it "should set and return a value, then return the same value" do value = "meow" expect(@vo.set_or_return(:test, value, {}).object_id).to eq(value.object_id) expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) end it "should set and return a default value when the argument is nil, then return the same value" do value = "meow" expect(@vo.set_or_return(:test, nil, { :default => value }).object_id).to eq(value.object_id) expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) end it "should raise an ArgumentError when argument is nil and required is true" do expect { @vo.set_or_return(:test, nil, { :required => true }) }.to raise_error(ArgumentError) end it "should not raise an error when argument is nil and required is false" do expect { @vo.set_or_return(:test, nil, { :required => false }) }.not_to raise_error end it "should set and return @name, then return @name for foo when argument is nil" do value = "meow" expect(@vo.set_or_return(:name, value, { }).object_id).to eq(value.object_id) expect(@vo.set_or_return(:foo, nil, { :name_attribute => true }).object_id).to eq(value.object_id) end it "should allow DelayedEvaluator instance to be set for value regardless of restriction" do value = Chef::DelayedEvaluator.new{ 'test' } @vo.set_or_return(:test, value, {:kind_of => Numeric}) end it "should raise an error when delayed evaluated attribute is not valid" do value = Chef::DelayedEvaluator.new{ 'test' } @vo.set_or_return(:test, value, {:kind_of => Numeric}) expect do @vo.set_or_return(:test, nil, {:kind_of => Numeric}) end.to raise_error(Chef::Exceptions::ValidationFailed) end it "should create DelayedEvaluator instance when #lazy is used" do @vo.set_or_return(:delayed, @vo.lazy{ 'test' }, {}) expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator) end it "should execute block on each call when DelayedEvaluator" do value = 'fubar' @vo.set_or_return(:test, @vo.lazy{ value }, {}) expect(@vo.set_or_return(:test, nil, {})).to eq('fubar') value = 'foobar' expect(@vo.set_or_return(:test, nil, {})).to eq('foobar') value = 'fauxbar' expect(@vo.set_or_return(:test, nil, {})).to eq('fauxbar') end it "should not evaluate non DelayedEvaluator instances" do value = lambda{ 'test' } @vo.set_or_return(:test, value, {}) expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id) expect(@vo.set_or_return(:test, nil, {})).to be_a(Proc) end end chef-12.3.0/spec/unit/mixin/powershell_type_coercions_spec.rb0000644000004100000410000000465212520074675024436 0ustar www-datawww-data# # Author:: Jay Mundrawala () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/mixin/powershell_type_coercions' require 'base64' class Chef::PSTypeTester include Chef::Mixin::PowershellTypeCoercions end describe Chef::Mixin::PowershellTypeCoercions do let (:test_class) { Chef::PSTypeTester.new } describe '#translate_type' do it 'should single quote a string' do expect(test_class.translate_type('foo')).to eq("'foo'") end ["'", '"', '#', '`'].each do |c| it "should base64 encode a string that contains #{c}" do expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c)) end end it 'should not quote an integer' do expect(test_class.translate_type(123)).to eq('123') end it 'should not quote a floating point number' do expect(test_class.translate_type(123.4)).to eq('123.4') end it 'should return $false when an instance of FalseClass is provided' do expect(test_class.translate_type(false)).to eq('$false') end it 'should return $true when an instance of TrueClass is provided' do expect(test_class.translate_type(true)).to eq('$true') end it 'should translate all members of a hash and wrap them in @{} separated by ;' do expect(test_class.translate_type({"a" => 1, "b" => 1.2, "c" => false, "d" => true })).to eq("@{a=1;b=1.2;c=$false;d=$true}") end it 'should translat all members of an array and them by a ,' do expect(test_class.translate_type([true, false])).to eq('@($true,$false)') end it 'should fall back :to_psobject if we have not defined at explicit rule' do ps_obj = double("PSObject") expect(ps_obj).to receive(:to_psobject).and_return('$true') expect(test_class.translate_type(ps_obj)).to eq('($true)') end end end chef-12.3.0/spec/unit/mixin/checksum_spec.rb0000644000004100000410000000237612520074675020750 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'spec_helper' require 'chef/mixin/checksum' require 'stringio' class Chef::CMCCheck include Chef::Mixin::Checksum end describe Chef::Mixin::Checksum do before(:each) do @checksum_user = Chef::CMCCheck.new @cache = Chef::Digester.instance @file = CHEF_SPEC_DATA + "/checksum/random.txt" @stat = double("File::Stat", { :mtime => Time.at(0) }) allow(File).to receive(:stat).and_return(@stat) end it "gets the checksum of a file" do expect(@checksum_user.checksum(@file)).to eq("09ee9c8cc70501763563bcf9c218d71b2fbf4186bf8e1e0da07f0f42c80a3394") end end chef-12.3.0/spec/unit/mixin/shell_out_spec.rb0000644000004100000410000003026312520074675021140 0ustar www-datawww-data# # Author:: Ho-Sheng Hsiao (hosh@opscode.com) # Code derived from spec/unit/mixin/command_spec.rb # # Original header: # Author:: Hongli Lai (hongli@phusion.nl) # Copyright:: Copyright (c) 2009 Phusion # 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::Mixin::ShellOut do let(:shell_out_class) { Class.new { include Chef::Mixin::ShellOut } } subject(:shell_out_obj) { shell_out_class.new } describe '#run_command_compatible_options' do subject { shell_out_obj.run_command_compatible_options(command_args) } let(:command_args) { [ cmd, options ] } let(:cmd) { "echo '#{rand(1000)}'" } let(:output) { StringIO.new } let!(:capture_log_output) { Chef::Log.logger = Logger.new(output) } let(:assume_deprecation_log_level) { allow(Chef::Log).to receive(:level).and_return(:warn) } context 'without options' do let(:command_args) { [ cmd ] } it 'should not edit command args' do is_expected.to eql(command_args) end end context 'without deprecated options' do let(:options) { { :environment => environment } } let(:environment) { { 'LC_ALL' => 'C', 'LANG' => 'C', 'LANGUAGE' => 'C' } } it 'should not edit command args' do is_expected.to eql(command_args) end end def self.should_emit_deprecation_warning_about(old_option, new_option) it 'should emit a deprecation warning' do assume_deprecation_log_level and capture_log_output subject expect(output.string).to match /DEPRECATION:/ expect(output.string).to match Regexp.escape(old_option.to_s) expect(output.string).to match Regexp.escape(new_option.to_s) end end context 'with :command_log_level option' do let(:options) { { :command_log_level => command_log_level } } let(:command_log_level) { :warn } it 'should convert :command_log_level to :log_level' do is_expected.to eql [ cmd, { :log_level => command_log_level } ] end should_emit_deprecation_warning_about :command_log_level, :log_level end context 'with :command_log_prepend option' do let(:options) { { :command_log_prepend => command_log_prepend } } let(:command_log_prepend) { 'PROVIDER:' } it 'should convert :command_log_prepend to :log_tag' do is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ] end should_emit_deprecation_warning_about :command_log_prepend, :log_tag end context "with 'command_log_level' option" do let(:options) { { 'command_log_level' => command_log_level } } let(:command_log_level) { :warn } it "should convert 'command_log_level' to :log_level" do is_expected.to eql [ cmd, { :log_level => command_log_level } ] end should_emit_deprecation_warning_about :command_log_level, :log_level end context "with 'command_log_prepend' option" do let(:options) { { 'command_log_prepend' => command_log_prepend } } let(:command_log_prepend) { 'PROVIDER:' } it "should convert 'command_log_prepend' to :log_tag" do is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ] end should_emit_deprecation_warning_about :command_log_prepend, :log_tag end end context "when testing individual methods" do before(:each) do @original_env = ENV.to_hash ENV.clear end after(:each) do ENV.clear ENV.update(@original_env) end let(:cmd) { "echo '#{rand(1000)}'" } describe "#shell_out" do describe "when the last argument is a Hash" do describe "and environment is an option" do it "should not change environment language settings when they are set to nil" do options = { :environment => { 'LC_ALL' => nil, 'LANGUAGE' => nil, 'LANG' => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not change environment language settings when they are set to non-nil" do options = { :environment => { 'LC_ALL' => 'en_US.UTF-8', 'LANGUAGE' => 'en_US.UTF-8', 'LANG' => 'en_US.UTF-8' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should set environment language settings to the configured internal locale when they are not present" do options = { :environment => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :environment => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], }, }).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not mutate the options hash when it adds language settings" do options = { :environment => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :environment => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], }, }).and_return(true) shell_out_obj.shell_out(cmd, options) expect(options[:environment].has_key?('LC_ALL')).to be false end end describe "and env is an option" do it "should not change env when langauge options are set to nil" do options = { :env => { 'LC_ALL' => nil, 'LANG' => nil, 'LANGUAGE' => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not change env when language options are set to non-nil" do options = { :env => { 'LC_ALL' => 'de_DE.UTF-8', 'LANG' => 'de_DE.UTF-8', 'LANGUAGE' => 'de_DE.UTF-8' }} expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should set environment language settings to the configured internal locale when they are not present" do options = { :env => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :env => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], } }).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not mutate the options hash when it adds language settings" do options = { :env => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :env => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], } }).and_return(true) shell_out_obj.shell_out(cmd, options) expect(options[:env].has_key?('LC_ALL')).to be false end end describe "and no env/environment option is present" do it "should set environment language settings to the configured internal locale" do options = { :user => 'morty' } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :user => 'morty', :environment => { 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], }, }).and_return(true) shell_out_obj.shell_out(cmd, options) end end end describe "when the last argument is not a Hash" do it "should set environment language settings to the configured internal locale" do expect(shell_out_obj).to receive(:shell_out_command).with(cmd, { :environment => { 'LC_ALL' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], }, }).and_return(true) shell_out_obj.shell_out(cmd) end end end describe "#shell_out_with_systems_locale" do describe "when the last argument is a Hash" do describe "and environment is an option" do it "should not change environment['LC_ALL'] when set to nil" do options = { :environment => { 'LC_ALL' => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end it "should not change environment['LC_ALL'] when set to non-nil" do options = { :environment => { 'LC_ALL' => 'en_US.UTF-8' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end it "should no longer set environment['LC_ALL'] to nil when 'LC_ALL' not present" do options = { :environment => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end end describe "and env is an option" do it "should not change env when set to nil" do options = { :env => { 'LC_ALL' => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end it "should not change env when set to non-nil" do options = { :env => { 'LC_ALL' => 'en_US.UTF-8'}} expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end it "should no longer set env['LC_ALL'] to nil when 'LC_ALL' not present" do options = { :env => { 'HOME' => '/Users/morty' } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end end describe "and no env/environment option is present" do it "should no longer add environment option and set environment['LC_ALL'] to nil" do options = { :user => 'morty' } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd, options) end end end describe "when the last argument is not a Hash" do it "should no longer add environment options and set environment['LC_ALL'] to nil" do expect(shell_out_obj).to receive(:shell_out_command).with(cmd).and_return(true) shell_out_obj.shell_out_with_systems_locale(cmd) end end end end end chef-12.3.0/spec/unit/mixin/deprecation_spec.rb0000644000004100000410000000346412520074675021442 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/deprecation' describe Chef::Mixin do describe "deprecating constants (Class/Module)" do before do Chef::Mixin.deprecate_constant(:DeprecatedClass, Chef::Node, "This is a test deprecation") @log_io = StringIO.new Chef::Log.init(@log_io) end it "has a list of deprecated constants" do expect(Chef::Mixin.deprecated_constants).to have_key(:DeprecatedClass) end it "returns the replacement when accessing the deprecated constant" do expect(Chef::Mixin::DeprecatedClass).to eq(Chef::Node) end it "warns when accessing the deprecated constant" do Chef::Mixin::DeprecatedClass expect(@log_io.string).to include("This is a test deprecation") end end end describe Chef::Mixin::Deprecation::DeprecatedInstanceVariable do before do Chef::Log.logger = Logger.new(StringIO.new) @deprecated_ivar = Chef::Mixin::Deprecation::DeprecatedInstanceVariable.new('value', 'an_ivar') end it "forward method calls to the target object" do expect(@deprecated_ivar.length).to eq(5) expect(@deprecated_ivar.to_sym).to eq(:value) end end chef-12.3.0/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb0000644000004100000410000000731712520074675026142 0ustar www-datawww-data# # Author:: Mark Mzyk () # 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 'spec_helper' require 'etc' require 'ostruct' describe Chef::Mixin::EnforceOwnershipAndPermissions do before(:each) do @node = Chef::Node.new @node.name "make_believe" @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @tmpdir = Dir.mktmpdir @resource = Chef::Resource::File.new("#{@tmpdir}/madeup.txt") FileUtils.touch @resource.path @resource.owner "adam" @provider = Chef::Provider::File.new(@resource, @run_context) @provider.current_resource = @resource end after(:each) do FileUtils.rm_rf(@tmpdir) end it "should call set_all on the file access control object" do expect_any_instance_of(Chef::FileAccessControl).to receive(:set_all) @provider.enforce_ownership_and_permissions end context "when nothing was updated" do before do allow_any_instance_of(Chef::FileAccessControl).to receive(:uid_from_resource).and_return(0) allow_any_instance_of(Chef::FileAccessControl).to receive(:requires_changes?).and_return(false) allow_any_instance_of(Chef::FileAccessControl).to receive(:define_resource_requirements) passwd_struct = if windows? Struct::Passwd.new("root", "x", 0, 0, "/root", "/bin/bash") else Struct::Passwd.new("root", "x", 0, 0, "root", "/root", "/bin/bash") end group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0) allow(Etc).to receive(:getpwuid).and_return(passwd_struct) allow(Etc).to receive(:getgrgid).and_return(group_struct) end it "does not set updated_by_last_action on the new resource" do expect(@provider.new_resource).not_to receive(:updated_by_last_action) allow_any_instance_of(Chef::FileAccessControl).to receive(:set_all) @provider.run_action(:create) end end context "when something was modified" do before do allow_any_instance_of(Chef::FileAccessControl).to receive(:requires_changes?).and_return(true) allow_any_instance_of(Chef::FileAccessControl).to receive(:uid_from_resource).and_return(0) passwd_struct = if windows? Struct::Passwd.new("root", "x", 0, 0, "/root", "/bin/bash") else Struct::Passwd.new("root", "x", 0, 0, "root", "/root", "/bin/bash") end group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0) allow(Etc).to receive(:getpwuid).and_return(passwd_struct) allow(Etc).to receive(:getgrgid).and_return(group_struct) end it "sets updated_by_last_action on the new resource" do @provider.new_resource.owner(0) # CHEF-3557 hack - Set these because we don't for windows @provider.new_resource.group(0) # CHEF-3557 hack - Set these because we don't for windows expect(@provider.new_resource).to receive(:updated_by_last_action) allow_any_instance_of(Chef::FileAccessControl).to receive(:set_all) @provider.run_action(:create) end end end chef-12.3.0/spec/unit/mixin/windows_architecture_helper_spec.rb0000644000004100000410000000633712520074675024742 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 | expect(valid_windows_architecture?(architecture)).to eq(true) end end it "returns false when invalid architectures are passed to valid_windows_architecture?" do @invalid_architectures.each do | architecture | expect(valid_windows_architecture?(architecture)).to eq(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 expect(assert_valid_windows_architecture!(architecture)).to 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 | expect(node_supports_windows_architecture?(new_node, supported_architecture)).to eq(true) if only_valid_combinations && (supported_architecture != :x86_64 && node_architecture != :i386 ) expect(node_supports_windows_architecture?(new_node, supported_architecture)).to eq(false) if ! only_valid_combinations && (supported_architecture == :x86_64 && node_architecture == :i386 ) end end end end chef-12.3.0/spec/unit/mixin/homebrew_user_spec.rb0000644000004100000410000000661312520074675022012 0ustar www-datawww-data# # Author:: Joshua Timberman () # # Copyright 2014, Chef Software, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' require 'chef/mixin/homebrew_user' class ExampleHomebrewUser include Chef::Mixin::HomebrewUser end describe Chef::Mixin::HomebrewUser do before(:each) do node.default['homebrew']['owner'] = nil end let(:homebrew_user) { ExampleHomebrewUser.new } let(:node) { Chef::Node.new } describe 'when the homebrew user is provided' do let(:uid) { 1001 } let(:user) { "foo" } it 'returns the homebrew user without looking at the file when uid is provided' do expect(File).to receive(:exist?).exactly(0).times expect(homebrew_user.find_homebrew_uid(uid)).to eq(uid) end it 'returns the homebrew user without looking at the file when name is provided' do expect(File).to receive(:exist?).exactly(0).times allow(Etc).to receive_message_chain(:getpwnam, :uid).and_return(uid) expect(homebrew_user.find_homebrew_uid(user)).to eq(uid) end end shared_examples "successfully find executable" do let(:user) { nil } let(:brew_owner) { 2001 } let(:default_brew_path) { '/usr/local/bin/brew' } let(:stat_double) { d = double() expect(d).to receive(:uid).and_return(brew_owner) d } context "debug statement prints owner name" do before do expect(Etc).to receive(:getpwuid).with(brew_owner).and_return(OpenStruct.new(:name => "name")) end it 'returns the owner of the brew executable when it is at a default location' do expect(File).to receive(:exist?).with(default_brew_path).and_return(true) expect(File).to receive(:stat).with(default_brew_path).and_return(stat_double) expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner) end it 'returns the owner of the brew executable when it is not at a default location' do expect(File).to receive(:exist?).with(default_brew_path).and_return(false) allow(homebrew_user).to receive_message_chain(:shell_out, :stdout, :strip).and_return("/foo") expect(File).to receive(:stat).with("/foo").and_return(stat_double) expect(homebrew_user.find_homebrew_uid(user)).to eq(brew_owner) end end end describe 'when the homebrew user is not provided' do it 'raises an error if no executable is found' do expect(File).to receive(:exist?).with(default_brew_path).and_return(false) allow(homebrew_user).to receive_message_chain(:shell_out, :stdout, :strip).and_return("") expect { homebrew_user.find_homebrew_uid(user) }.to raise_error(Chef::Exceptions::CannotDetermineHomebrewOwner) end include_examples "successfully find executable" context "the executable is owned by root" do include_examples "successfully find executable" do let(:brew_owner) { 0 } end end end end chef-12.3.0/spec/unit/mixin/securable_spec.rb0000644000004100000410000004054512520074675021113 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Mark Mzyk () # 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 'spec_helper' describe Chef::Mixin::Securable do before(:each) do @securable = Object.new @securable.send(:extend, Chef::Mixin::Securable) @securable.send(:extend, Chef::Mixin::ParamsValidate) end it "should accept a group name or id for group" do expect { @securable.group "root" }.not_to raise_error expect { @securable.group 123 }.not_to raise_error expect { @securable.group "+bad:group" }.to raise_error(ArgumentError) end it "should accept a user name or id for owner" do expect { @securable.owner "root" }.not_to raise_error expect { @securable.owner 123 }.not_to raise_error expect { @securable.owner "+bad:owner" }.to raise_error(ArgumentError) end it "allows the owner to be specified as #user" do expect(@securable).to respond_to(:user) end describe "unix-specific behavior" do before(:each) do platform_mock :unix do load File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "chef", "mixin", "securable.rb") @securable = Object.new @securable.send(:extend, Chef::Mixin::Securable) @securable.send(:extend, Chef::Mixin::ParamsValidate) end end it "should accept group/owner names with spaces and backslashes" do expect { @securable.group 'test\ group' }.not_to raise_error expect { @securable.owner 'test\ group' }.not_to raise_error end it "should accept group/owner names that are a single character or digit" do expect { @securable.group 'v' }.not_to raise_error expect { @securable.group '1' }.not_to raise_error expect { @securable.owner 'v' }.not_to raise_error expect { @securable.owner '1' }.not_to raise_error end it "should not accept group/owner names starting with '-', '+', or '~'" do expect { @securable.group '-test' }.to raise_error(ArgumentError) expect { @securable.group '+test' }.to raise_error(ArgumentError) expect { @securable.group '~test' }.to raise_error(ArgumentError) expect { @securable.group 'te-st' }.not_to raise_error expect { @securable.group 'te+st' }.not_to raise_error expect { @securable.group 'te~st' }.not_to raise_error expect { @securable.owner '-test' }.to raise_error(ArgumentError) expect { @securable.owner '+test' }.to raise_error(ArgumentError) expect { @securable.owner '~test' }.to raise_error(ArgumentError) expect { @securable.owner 'te-st' }.not_to raise_error expect { @securable.owner 'te+st' }.not_to raise_error expect { @securable.owner 'te~st' }.not_to raise_error end it "should not accept group/owner names containing ':', ',' or non-space whitespace" do expect { @securable.group ':test' }.to raise_error(ArgumentError) expect { @securable.group 'te:st' }.to raise_error(ArgumentError) expect { @securable.group ',test' }.to raise_error(ArgumentError) expect { @securable.group 'te,st' }.to raise_error(ArgumentError) expect { @securable.group "\ttest" }.to raise_error(ArgumentError) expect { @securable.group "te\tst" }.to raise_error(ArgumentError) expect { @securable.group "\rtest" }.to raise_error(ArgumentError) expect { @securable.group "te\rst" }.to raise_error(ArgumentError) expect { @securable.group "\ftest" }.to raise_error(ArgumentError) expect { @securable.group "te\fst" }.to raise_error(ArgumentError) expect { @securable.group "\0test" }.to raise_error(ArgumentError) expect { @securable.group "te\0st" }.to raise_error(ArgumentError) expect { @securable.owner ':test' }.to raise_error(ArgumentError) expect { @securable.owner 'te:st' }.to raise_error(ArgumentError) expect { @securable.owner ',test' }.to raise_error(ArgumentError) expect { @securable.owner 'te,st' }.to raise_error(ArgumentError) expect { @securable.owner "\ttest" }.to raise_error(ArgumentError) expect { @securable.owner "te\tst" }.to raise_error(ArgumentError) expect { @securable.owner "\rtest" }.to raise_error(ArgumentError) expect { @securable.owner "te\rst" }.to raise_error(ArgumentError) expect { @securable.owner "\ftest" }.to raise_error(ArgumentError) expect { @securable.owner "te\fst" }.to raise_error(ArgumentError) expect { @securable.owner "\0test" }.to raise_error(ArgumentError) expect { @securable.owner "te\0st" }.to raise_error(ArgumentError) end it "should accept Active Directory-style domain names pulled in via LDAP (on unix hosts)" do expect { @securable.owner "domain\@user" }.not_to raise_error expect { @securable.owner "domain\\user" }.not_to raise_error expect { @securable.group "domain\@group" }.not_to raise_error expect { @securable.group "domain\\group" }.not_to raise_error expect { @securable.group "domain\\group^name" }.not_to raise_error end it "should not accept group/owner names containing embedded carriage returns" do skip "XXX: params_validate needs to be extended to support multi-line regex" #lambda { @securable.group "\ntest" }.should raise_error(ArgumentError) #lambda { @securable.group "te\nst" }.should raise_error(ArgumentError) #lambda { @securable.owner "\ntest" }.should raise_error(ArgumentError) #lambda { @securable.owner "te\nst" }.should raise_error(ArgumentError) end it "should accept group/owner names in UTF-8" do expect { @securable.group 'tëst' }.not_to raise_error expect { @securable.group 'ë' }.not_to raise_error expect { @securable.owner 'tëst' }.not_to raise_error expect { @securable.owner 'ë' }.not_to raise_error end it "should accept a unix file mode in string form as an octal number" do expect { @securable.mode "0" }.not_to raise_error expect { @securable.mode "0000" }.not_to raise_error expect { @securable.mode "0111" }.not_to raise_error expect { @securable.mode "0444" }.not_to raise_error expect { @securable.mode "111" }.not_to raise_error expect { @securable.mode "444" }.not_to raise_error expect { @securable.mode "7777" }.not_to raise_error expect { @securable.mode "07777" }.not_to raise_error expect { @securable.mode "-01" }.to raise_error(ArgumentError) expect { @securable.mode "010000" }.to raise_error(ArgumentError) expect { @securable.mode "-1" }.to raise_error(ArgumentError) expect { @securable.mode "10000" }.to raise_error(ArgumentError) expect { @securable.mode "07778" }.to raise_error(ArgumentError) expect { @securable.mode "7778" }.to raise_error(ArgumentError) expect { @securable.mode "4095" }.to raise_error(ArgumentError) expect { @securable.mode "0foo1234" }.to raise_error(ArgumentError) expect { @securable.mode "foo1234" }.to raise_error(ArgumentError) end it "should accept a unix file mode in numeric form as a ruby-interpreted integer" do expect { @securable.mode(0) }.not_to raise_error expect { @securable.mode(0000) }.not_to raise_error expect { @securable.mode(444) }.not_to raise_error expect { @securable.mode(0444) }.not_to raise_error expect { @securable.mode(07777) }.not_to raise_error expect { @securable.mode(292) }.not_to raise_error expect { @securable.mode(4095) }.not_to raise_error expect { @securable.mode(0111) }.not_to raise_error expect { @securable.mode(73) }.not_to raise_error expect { @securable.mode(-01) }.to raise_error(ArgumentError) expect { @securable.mode(010000) }.to raise_error(ArgumentError) expect { @securable.mode(-1) }.to raise_error(ArgumentError) expect { @securable.mode(4096) }.to raise_error(ArgumentError) end end describe "windows-specific behavior" do before(:each) do platform_mock :windows do load File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "chef", "mixin", "securable.rb") securable_class = Class.new do include Chef::Mixin::Securable include Chef::Mixin::ParamsValidate end @securable = securable_class.new end end it "should not accept a group name or id for group with spaces and multiple backslashes" do expect { @securable.group 'test\ \group' }.to raise_error(ArgumentError) end it "should accept a unix file mode in string form as an octal number" do expect { @securable.mode "0" }.not_to raise_error expect { @securable.mode "0000" }.not_to raise_error expect { @securable.mode "0111" }.not_to raise_error expect { @securable.mode "0444" }.not_to raise_error expect { @securable.mode "111" }.not_to raise_error expect { @securable.mode "444" }.not_to raise_error expect { @securable.mode "7777" }.to raise_error(ArgumentError) expect { @securable.mode "07777" }.to raise_error(ArgumentError) expect { @securable.mode "-01" }.to raise_error(ArgumentError) expect { @securable.mode "010000" }.to raise_error(ArgumentError) expect { @securable.mode "-1" }.to raise_error(ArgumentError) expect { @securable.mode "10000" }.to raise_error(ArgumentError) expect { @securable.mode "07778" }.to raise_error(ArgumentError) expect { @securable.mode "7778" }.to raise_error(ArgumentError) expect { @securable.mode "4095" }.to raise_error(ArgumentError) expect { @securable.mode "0foo1234" }.to raise_error(ArgumentError) expect { @securable.mode "foo1234" }.to raise_error(ArgumentError) end it "should accept a unix file mode in numeric form as a ruby-interpreted integer" do expect { @securable.mode 0 }.not_to raise_error expect { @securable.mode 0000 }.not_to raise_error expect { @securable.mode 444 }.not_to raise_error expect { @securable.mode 0444 }.not_to raise_error expect { @securable.mode 07777 }.to raise_error(ArgumentError) expect { @securable.mode 292 }.not_to raise_error expect { @securable.mode 4095 }.to raise_error(ArgumentError) expect { @securable.mode 0111 }.not_to raise_error expect { @securable.mode 73 }.not_to raise_error expect { @securable.mode -01 }.to raise_error(ArgumentError) expect { @securable.mode 010000 }.to raise_error(ArgumentError) expect { @securable.mode -1 }.to raise_error(ArgumentError) expect { @securable.mode 4096 }.to raise_error(ArgumentError) end it "should allow you to specify :full_control, :modify, :read_execute, :read, and :write rights" do expect { @securable.rights :full_control, "The Dude" }.not_to raise_error expect { @securable.rights :modify, "The Dude" }.not_to raise_error expect { @securable.rights :read_execute, "The Dude" }.not_to raise_error expect { @securable.rights :read, "The Dude" }.not_to raise_error expect { @securable.rights :write, "The Dude" }.not_to raise_error expect { @securable.rights :to_party, "The Dude" }.to raise_error(ArgumentError) end it "should allow you to specify :full_control, :modify, :read_execute, :read, and :write deny_rights" do expect { @securable.deny_rights :full_control, "The Dude" }.not_to raise_error expect { @securable.deny_rights :modify, "The Dude" }.not_to raise_error expect { @securable.deny_rights :read_execute, "The Dude" }.not_to raise_error expect { @securable.deny_rights :read, "The Dude" }.not_to raise_error expect { @securable.deny_rights :write, "The Dude" }.not_to raise_error expect { @securable.deny_rights :to_party, "The Dude" }.to raise_error(ArgumentError) end it "should accept a principal as a string or an array" do expect { @securable.rights :read, "The Dude" }.not_to raise_error expect { @securable.rights :read, ["The Dude","Donny"] }.not_to raise_error expect { @securable.rights :read, 3 }.to raise_error(ArgumentError) end it "should allow you to specify whether the permissions applies_to_children with true/false/:containers_only/:objects_only" do expect { @securable.rights :read, "The Dude", :applies_to_children => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => :containers_only }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => :objects_only }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => 'poop' }.to raise_error(ArgumentError) end it "should allow you to specify whether the permissions applies_to_self with true/false" do expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_self => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_self => 'poop' }.to raise_error(ArgumentError) end it "should allow you to specify whether the permissions applies one_level_deep with true/false" do expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => 'poop' }.to raise_error(ArgumentError) end it "should allow multiple rights and deny_rights declarations" do @securable.rights :read, "The Dude" @securable.deny_rights :full_control, "The Dude" @securable.rights :full_control, "The Dude" @securable.rights :write, "The Dude" @securable.deny_rights :read, "The Dude" expect(@securable.rights.size).to eq(3) expect(@securable.deny_rights.size).to eq(2) end it "should allow you to specify whether the permission applies_to_self only if you specified applies_to_children" do expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => true, :applies_to_self => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => false, :applies_to_self => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => false, :applies_to_self => false }.to raise_error(ArgumentError) expect { @securable.rights :read, "The Dude", :applies_to_self => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_self => false }.not_to raise_error end it "should allow you to specify whether the permission applies one_level_deep only if you specified applies_to_children" do expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => true, :one_level_deep => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :applies_to_children => false, :one_level_deep => true }.to raise_error(ArgumentError) expect { @securable.rights :read, "The Dude", :applies_to_children => false, :one_level_deep => false }.not_to raise_error expect { @securable.rights :read, "The Dude", :one_level_deep => true }.not_to raise_error expect { @securable.rights :read, "The Dude", :one_level_deep => false }.not_to raise_error end it "should allow you to specify whether the permissions inherit with true/false" do expect { @securable.inherits true }.not_to raise_error expect { @securable.inherits false }.not_to raise_error expect { @securable.inherits "monkey" }.to raise_error(ArgumentError) end end end chef-12.3.0/spec/unit/mixin/deep_merge_spec.rb0000644000004100000410000003634212520074675021242 0ustar www-datawww-data# # Author:: Matthew Kent () # Author:: Steve Midgley (http://www.misuse.org/science) # Copyright:: Copyright (c) 2010 Matthew Kent # Copyright:: Copyright (c) 2008 Steve Midgley # 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. # Notice: # This code is imported from deep_merge by Steve Midgley. deep_merge is # available under the MIT license from # http://trac.misuse.org/science/wiki/DeepMerge require 'spec_helper' # Test coverage from the original author converted to rspec describe Chef::Mixin::DeepMerge, "deep_merge!" do before do @dm = Chef::Mixin::DeepMerge @field_ko_prefix = '!merge' end # deep_merge core tests - moving from basic to more complex it "tests merging an hash w/array into blank hash" do hash_src = {'id' => '2'} hash_dst = {} @dm.deep_merge!(hash_src.dup, hash_dst) expect(hash_dst).to eq(hash_src) end it "tests merging an hash w/array into blank hash" do hash_src = {'region' => {'id' => ['227', '2']}} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq(hash_src) end it "tests merge from empty hash" do hash_src = {} hash_dst = {"property" => ["2","4"]} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => ["2","4"]}) end it "tests merge to empty hash" do hash_src = {"property" => ["2","4"]} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => ["2","4"]}) end it "tests simple string overwrite" do hash_src = {"name" => "value"} hash_dst = {"name" => "value1"} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"name" => "value"}) end it "tests simple string overwrite of empty hash" do hash_src = {"name" => "value"} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq(hash_src) end it "tests hashes holding array" do hash_src = {"property" => ["1","3"]} hash_dst = {"property" => ["2","4"]} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => ["2","4","1","3"]}) end it "tests hashes holding hashes holding arrays (array with duplicate elements is merged with dest then src" do hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["3", "2"], "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => ["3","2","1"], "bathroom_count" => ["2", "1", "4+"]}}) end it "tests hash holding hash holding array v string (string is overwritten by array)" do hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}) end it "tests hash holding hash holding string v array (array is overwritten by string)" do hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}) end it "tests hash holding hash holding hash v array (array is overwritten by hash)" do hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}) end it "tests 3 hash layers holding integers (integers are overwritten by source)" do hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => 2, "queen_bed" => 4}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}) end it "tests 3 hash layers holding arrays of int (arrays are merged)" do hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => ["1", "4+"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2","1","4+"]}}) end it "tests 1 hash overwriting 3 hash layers holding arrays of int" do hash_src = {"property" => "1"} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => "1"}) end it "tests 3 hash layers holding arrays of int (arrays are merged) but second hash's array is overwritten" do hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => "1"}}) end it "tests 3 hash layers holding arrays of int, but one holds int. This one overwrites, but the rest merge" do hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [1]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [4,1]}, "bathroom_count" => ["2","1"]}}) end it "tests 3 hash layers holding arrays of int, but source is incomplete." do hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}) end it "tests 3 hash layers holding arrays of int, but source is shorter and has new 2nd level ints." do hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {2=>3, "king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}) end it "tests 3 hash layers holding arrays of int, but source is empty" do hash_src = {} hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}) end it "tests 3 hash layers holding arrays of int, but dest is empty" do hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}) end it "tests hash holding arrays of arrays" do hash_src = {["1", "2", "3"] => ["1", "2"]} hash_dst = {["4", "5"] => ["3"]} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({["1","2","3"] => ["1", "2"], ["4", "5"] => ["3"]}) end it "tests merging of hash with blank hash, and make sure that source array split does not function when turned off" do hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({'property' => {'bedroom_count' => ["1","2,3"]}}) end it "tests merging into a blank hash" do hash_src = {"action"=>"browse", "controller"=>"results"} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq(hash_src) end it "tests are unmerged hashes passed unmodified w/out :unpack_arrays?" do hash_src = {"amenity"=>{"id"=>["26,27"]}} hash_dst = {} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"amenity"=>{"id"=>["26,27"]}}) end it "tests hash of array of hashes" do hash_src = {"item" => [{"1" => "3"}, {"2" => "4"}]} hash_dst = {"item" => [{"3" => "5"}]} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"item" => [{"3" => "5"}, {"1" => "3"}, {"2" => "4"}]}) end # Additions since import it "should overwrite true with false when merging boolean values" do hash_src = {"valid" => false} hash_dst = {"valid" => true} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"valid" => false}) end it "should overwrite false with true when merging boolean values" do hash_src = {"valid" => true} hash_dst = {"valid" => false} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"valid" => true}) end it "should overwrite a string with an empty string when merging string values" do hash_src = {"item" => " "} hash_dst = {"item" => "orange"} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"item" => " "}) end it "should overwrite an empty string with a string when merging string values" do hash_src = {"item" => "orange"} hash_dst = {"item" => " "} @dm.deep_merge!(hash_src, hash_dst) expect(hash_dst).to eq({"item" => "orange"}) end end # deep_merge! # Chef specific describe Chef::Mixin::DeepMerge do before do @dm = Chef::Mixin::DeepMerge end describe "merge" do it "should merge a hash into an empty hash" do hash_dst = {} hash_src = {'id' => '2'} expect(@dm.merge(hash_dst, hash_src)).to eq(hash_src) end it "should merge a nested hash into an empty hash" do hash_dst = {} hash_src = {'region' => {'id' => ['227', '2']}} expect(@dm.merge(hash_dst, hash_src)).to eq(hash_src) end it "should overwrite as string value when merging hashes" do hash_dst = {"name" => "value1"} hash_src = {"name" => "value"} expect(@dm.merge(hash_dst, hash_src)).to eq({"name" => "value"}) end it "should merge arrays within hashes" do hash_dst = {"property" => ["2","4"]} hash_src = {"property" => ["1","3"]} expect(@dm.merge(hash_dst, hash_src)).to eq({"property" => ["2","4","1","3"]}) end it "should merge deeply nested hashes" do hash_dst = {"property" => {"values" => {"are" => "falling", "can" => "change"}}} hash_src = {"property" => {"values" => {"are" => "stable", "may" => "rise"}}} expect(@dm.merge(hash_dst, hash_src)).to eq({"property" => {"values" => {"are" => "stable", "can" => "change", "may" => "rise"}}}) end it "should not modify the source or destination during the merge" do hash_dst = {"property" => ["1","2","3"]} hash_src = {"property" => ["4","5","6"]} ret = @dm.merge(hash_dst, hash_src) expect(hash_dst).to eq({"property" => ["1","2","3"]}) expect(hash_src).to eq({"property" => ["4","5","6"]}) expect(ret).to eq({"property" => ["1","2","3","4","5","6"]}) end it "should not error merging un-dupable objects" do @dm.deep_merge(nil, 4) end end describe "hash-only merging" do it "merges Hashes like normal deep merge" do merge_ee_hash = {"top_level_a" => {"1_deep_a" => "1-a-merge-ee", "1_deep_b" => "1-deep-b-merge-ee"}, "top_level_b" => "top-level-b-merge-ee"} merge_with_hash = {"top_level_a" => {"1_deep_b" => "1-deep-b-merged-onto", "1_deep_c" => "1-deep-c-merged-onto"}, "top_level_b" => "top-level-b-merged-onto" } merged_result = @dm.hash_only_merge(merge_ee_hash, merge_with_hash) expect(merged_result["top_level_b"]).to eq("top-level-b-merged-onto") expect(merged_result["top_level_a"]["1_deep_a"]).to eq("1-a-merge-ee") expect(merged_result["top_level_a"]["1_deep_b"]).to eq("1-deep-b-merged-onto") expect(merged_result["top_level_a"]["1_deep_c"]).to eq("1-deep-c-merged-onto") end it "replaces arrays rather than merging them" do merge_ee_hash = {"top_level_a" => {"1_deep_a" => "1-a-merge-ee", "1_deep_b" => %w[A A A]}, "top_level_b" => "top-level-b-merge-ee"} merge_with_hash = {"top_level_a" => {"1_deep_b" => %w[B B B], "1_deep_c" => "1-deep-c-merged-onto"}, "top_level_b" => "top-level-b-merged-onto" } merged_result = @dm.hash_only_merge(merge_ee_hash, merge_with_hash) expect(merged_result["top_level_b"]).to eq("top-level-b-merged-onto") expect(merged_result["top_level_a"]["1_deep_a"]).to eq("1-a-merge-ee") expect(merged_result["top_level_a"]["1_deep_b"]).to eq(%w[B B B]) end it "replaces non-hash items with hashes when there's a conflict" do merge_ee_hash = {"top_level_a" => "top-level-a-mergee", "top_level_b" => "top-level-b-merge-ee"} merge_with_hash = {"top_level_a" => {"1_deep_b" => %w[B B B], "1_deep_c" => "1-deep-c-merged-onto"}, "top_level_b" => "top-level-b-merged-onto" } merged_result = @dm.hash_only_merge(merge_ee_hash, merge_with_hash) expect(merged_result["top_level_a"]).to be_a(Hash) expect(merged_result["top_level_a"]["1_deep_a"]).to be_nil expect(merged_result["top_level_a"]["1_deep_b"]).to eq(%w[B B B]) end it "does not mutate deeply-nested original hashes by default" do merge_ee_hash = {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_a" => "foo" }}}} merge_with_hash = {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_b" => "bar" }}}} @dm.hash_only_merge(merge_ee_hash, merge_with_hash) expect(merge_ee_hash).to eq({"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_a" => "foo" }}}}) expect(merge_with_hash).to eq({"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_b" => "bar" }}}}) end it "does not error merging un-dupable items" do merge_ee_hash = {"top_level_a" => 1, "top_level_b" => false} merge_with_hash = {"top_level_a" => 2, "top_level_b" => true } @dm.hash_only_merge(merge_ee_hash, merge_with_hash) end end end chef-12.3.0/spec/unit/mixin/path_sanity_spec.rb0000644000004100000410000000651612520074675021471 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' class PathSanityTestHarness include Chef::Mixin::PathSanity end describe Chef::Mixin::PathSanity do before do @sanity = PathSanityTestHarness.new end describe "when enforcing path sanity" do before do Chef::Config[:enforce_path_sanity] = true @ruby_bindir = '/some/ruby/bin' @gem_bindir = '/some/gem/bin' allow(Gem).to receive(:bindir).and_return(@gem_bindir) allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return(@ruby_bindir) allow(Chef::Platform).to receive(:windows?).and_return(false) end it "adds all useful PATHs even if environment is an empty hash" do env={} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "adds all useful PATHs that are not yet in PATH to PATH" do env = {"PATH" => ""} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "does not re-add paths that already exist in PATH" do env = {"PATH" => "/usr/bin:/sbin:/bin"} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("/usr/bin:/sbin:/bin:#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin") end it "adds the current executing Ruby's bindir and Gem bindir to the PATH" do env = {"PATH" => ""} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "does not create entries for Ruby/Gem bindirs if they exist in SANE_PATH or PATH" do ruby_bindir = '/usr/bin' gem_bindir = '/yo/gabba/gabba' allow(Gem).to receive(:bindir).and_return(gem_bindir) allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return(ruby_bindir) env = {"PATH" => gem_bindir} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("/yo/gabba/gabba:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "builds a valid windows path" do ruby_bindir = 'C:\ruby\bin' gem_bindir = 'C:\gems\bin' allow(Gem).to receive(:bindir).and_return(gem_bindir) allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return(ruby_bindir) allow(Chef::Platform).to receive(:windows?).and_return(true) env = {"PATH" => 'C:\Windows\system32;C:\mr\softie'} @sanity.enforce_path_sanity(env) expect(env["PATH"]).to eq("C:\\Windows\\system32;C:\\mr\\softie;#{ruby_bindir};#{gem_bindir}") end end end chef-12.3.0/spec/unit/mixin/command_spec.rb0000644000004100000410000000706212520074675020561 0ustar www-datawww-data# # Author:: Hongli Lai (hongli@phusion.nl) # Copyright:: Copyright (c) 2009 Phusion # 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::Mixin::Command, :volatile do if windows? pending("TODO MOVE: this is a platform specific integration test.") else describe "popen4" do include Chef::Mixin::Command it "should be possible to read the child process's stdout and stderr" do popen4("sh -c 'echo hello && echo world >&2'") do |pid, stdin, stdout, stderr| expect(stdout.read).to eq("hello\n") expect(stderr.read).to eq("world\n") end end it "should default all commands to be run in the POSIX standard C locale" do popen4("echo $LC_ALL") do |pid, stdin, stdout, stderr| expect(stdout.read.strip).to eq("C") end end it "should respect locale when specified explicitly" do popen4("echo $LC_ALL", :environment => {"LC_ALL" => "es"}) do |pid, stdin, stdout, stderr| expect(stdout.read.strip).to eq("es") end end it "should end when the child process reads from STDIN and a block is given" do expect {Timeout.timeout(10) do popen4("ruby -e 'while gets; end'", :waitlast => true) do |pid, stdin, stdout, stderr| (1..5).each { |i| stdin.puts "#{i}" } end end }.not_to raise_error end describe "when a process detaches but doesn't close STDOUT and STDERR [CHEF-584]" do it "returns immediately after the first child process exits" do expect {Timeout.timeout(10) do pid, stdin,stdout,stderr = nil,nil,nil,nil evil_forker="exit if fork; 10.times { sleep 1}" popen4("ruby -e '#{evil_forker}'") do |pid,stdin,stdout,stderr| end end}.not_to raise_error end end end describe "run_command" do include Chef::Mixin::Command it "logs the command's stderr and stdout output if the command failed" do allow(Chef::Log).to receive(:level).and_return(:debug) begin run_command(:command => "sh -c 'echo hello; echo world >&2; false'") violated "Exception expected, but nothing raised." rescue => e expect(e.message).to match(/STDOUT: hello/) expect(e.message).to match(/STDERR: world/) end end describe "when a process detaches but doesn't close STDOUT and STDERR [CHEF-584]" do it "returns successfully" do # CHEF-2916 might have added a slight delay here, or our CI # infrastructure is burdened. Bumping timeout from 2 => 4 -- # btm # Serdar - During Solaris tests, we've seen that processes # are taking a long time to exit. Bumping timeout now to 10. expect {Timeout.timeout(10) do evil_forker="exit if fork; 10.times { sleep 1}" run_command(:command => "ruby -e '#{evil_forker}'") end}.not_to raise_error end end end end end chef-12.3.0/spec/unit/mixin/xml_escape_spec.rb0000644000004100000410000000304712520074675021262 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' class XMLEscapingTestHarness include Chef::Mixin::XMLEscape end describe Chef::Mixin::XMLEscape do before do @escaper = XMLEscapingTestHarness.new end it "escapes ampersands to '&'" do expect(@escaper.xml_escape("&")).to eq("&") end it "escapes angle brackets to < or >" do expect(@escaper.xml_escape("<")).to eq("<") expect(@escaper.xml_escape(">")).to eq(">") end it "does not modify ASCII strings" do expect(@escaper.xml_escape('foobarbaz!@#$%^*()')).to eq('foobarbaz!@#$%^*()') end it "converts invalid bytes to asterisks" do expect(@escaper.xml_escape("\x00")).to eq("*") end it "converts UTF-8 correctly" do expect(@escaper.xml_escape("\xC2\xA9")).to eq('©') end it "converts win 1252 characters correctly" do expect(@escaper.xml_escape("\x80")).to eq('€') end end chef-12.3.0/spec/unit/mixin/convert_to_class_name_spec.rb0000644000004100000410000000355112520074675023511 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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' class ConvertToClassTestHarness include Chef::Mixin::ConvertToClassName end describe Chef::Mixin::ConvertToClassName do before do @convert = ConvertToClassTestHarness.new end it "converts a_snake_case_word to a CamelCaseWord" do expect(@convert.convert_to_class_name("now_camelized")).to eq("NowCamelized") end it "converts a CamelCaseWord to a snake_case_word" do expect(@convert.convert_to_snake_case("NowImASnake")).to eq("now_im_a_snake") end it "removes the base classes before snake casing" do expect(@convert.convert_to_snake_case("NameSpaced::Class::ThisIsWin", "NameSpaced::Class")).to eq("this_is_win") end it "removes the base classes without explicitly naming them and returns snake case" do expect(@convert.snake_case_basename("NameSpaced::Class::ExtraWin")).to eq("extra_win") end it "interprets non-alphanumeric characters in snake case as word boundaries" do expect(@convert.convert_to_class_name("now_camelized_without-hyphen")).to eq("NowCamelizedWithoutHyphen") end it "interprets underscore" do expect(@convert.convert_to_class_name("_remove_leading_underscore")).to eq("RemoveLeadingUnderscore") end end chef-12.3.0/spec/unit/mixin/template_spec.rb0000644000004100000410000002360312520074675020755 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'cgi' describe Chef::Mixin::Template, "render_template" do let(:sep) { Chef::Platform.windows? ? "\r\n" : "\n" } before :each do @context = Chef::Mixin::Template::TemplateContext.new({}) end it "should render the template evaluated in the given context" do @context[:foo] = "bar" output = @context.render_template_from_string("<%= @foo %>") expect(output).to eq("bar") end template_contents = [ "Fancy\r\nTemplate\r\n\r\n", "Fancy\nTemplate\n\n", "Fancy\r\nTemplate\n\r\n"] describe "when running on windows" do before do allow(Chef::Platform).to receive(:windows?).and_return(true) end it "should render the templates with windows line endings" do template_contents.each do |template_content| output = @context.render_template_from_string(template_content) output.each_line do |line| expect(line).to end_with("\r\n") end end end end describe "when running on unix" do before do allow(Chef::Platform).to receive(:windows?).and_return(false) end it "should render the templates with unix line endings" do template_contents.each do |template_content| output = @context.render_template_from_string(template_content) output.each_line do |line| expect(line).to end_with("\n") end end end end it "should provide a node method to access @node" do @context[:node] = "tehShizzle" output = @context.render_template_from_string("<%= @node %>") expect(output).to eq("tehShizzle") end describe "with a template resource" do before :each do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) cl.load_cookbooks @cookbook_collection = Chef::CookbookCollection.new(cl) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @rendered_file_location = Dir.tmpdir + '/openldap_stuff.conf' @resource = Chef::Resource::Template.new(@rendered_file_location) @resource.cookbook_name = 'openldap' @current_resource = @resource.dup @content_provider = Chef::Provider::Template::Content.new(@resource, @current_resource, @run_context) @template_context = Chef::Mixin::Template::TemplateContext.new({}) @template_context[:node] = @node @template_context[:template_finder] = Chef::Provider::TemplateFinder.new(@run_context, @resource.cookbook_name, @node) end it "should provide a render method" do output = @template_context.render_template_from_string("before {<%= render('test.erb').strip -%>} after") expect(output).to eq("before {We could be diving for pearls!} after") end it "should render local files" do begin tf = Tempfile.new("partial") tf.write "test" tf.rewind output = @template_context.render_template_from_string("before {<%= render '#{tf.path}', :local => true %>} after") expect(output).to eq("before {test} after") ensure tf.close end end it "should render partials from a different cookbook" do @template_context[:template_finder] = Chef::Provider::TemplateFinder.new(@run_context, 'apache2', @node) output = @template_context.render_template_from_string("before {<%= render('test.erb', :cookbook => 'openldap').strip %>} after") expect(output).to eq("before {We could be diving for pearls!} after") end it "should render using the source argument if provided" do begin tf = Tempfile.new("partial") tf.write "test" tf.rewind output = @template_context.render_template_from_string("before {<%= render 'something', :local => true, :source => '#{tf.path}' %>} after") expect(output).to eq("before {test} after") ensure tf.close end end it "should pass the node to partials" do @node.normal[:slappiness] = "happiness" output = @template_context.render_template_from_string("before {<%= render 'openldap_stuff.conf.erb' %>} after") expect(output).to eq("before {slappiness is happiness} after") end it "should pass the original variables to partials" do @template_context[:secret] = 'candy' output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after") output == "before {super secret is candy} after" end it "should pass variables to partials" do output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'whatever' } %>} after") expect(output).to eq("before {super secret is whatever} after") end it "should pass variables to partials even if they are named the same" do @template_context[:secret] = 'one' output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'two' } %>} after <%= @secret %>") expect(output).to eq("before {super secret is two} after one") end it "should pass nil for missing variables in partials" do output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {} %>} after") expect(output).to eq("before {super secret is } after") output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb' %>} after") expect(output).to eq("before {super secret is } after") end it "should render nested partials" do path = File.expand_path(File.join(CHEF_SPEC_DATA, "partial_one.erb")) output = @template_context.render_template_from_string("before {<%= render('#{path}', :local => true).strip %>} after") expect(output).to eq("before {partial one We could be diving for pearls! calling home} after") end describe "when customizing the template context" do it "extends the context to include modules" do mod = Module.new do def hello "ohai" end end @template_context._extend_modules([mod]) output = @template_context.render_template_from_string("<%=hello%>") expect(output).to eq("ohai") end it "emits a warning when overriding 'core' methods" do mod = Module.new do def render end def node end def render_template end def render_template_from_string end end ['node', 'render', 'render_template', 'render_template_from_string'].each do |method_name| expect(Chef::Log).to receive(:warn).with(/^Core template method `#{method_name}' overridden by extension module/) end @template_context._extend_modules([mod]) end end end describe "when an exception is raised in the template" do def do_raise @context.render_template_from_string("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno") end it "should catch and re-raise the exception as a TemplateError" do expect { do_raise }.to raise_error(Chef::Mixin::Template::TemplateError) end it "should raise an error if an attempt is made to access node but it is nil" do expect {@context.render_template_from_string("<%= node %>") {|r| r}}.to raise_error(Chef::Mixin::Template::TemplateError) end describe "the raised TemplateError" do before :each do begin do_raise rescue Chef::Mixin::Template::TemplateError => e @exception = e end end it "should have the original exception" do expect(@exception.original_exception).to be expect(@exception.original_exception.message).to match(/undefined local variable or method `this_is_not_defined'/) end it "should determine the line number of the exception" do expect(@exception.line_number).to eq(4) end it "should provide a source listing of the template around the exception" do expect(@exception.source_listing).to eq(" 2: bar\n 3: baz\n 4: <%= this_is_not_defined %>\n 5: quin\n 6: qunx") end it "should provide the evaluation context of the template" do expect(@exception.context).to eq(@context) end it "should defer the message to the original exception" do expect(@exception.message).to match(/undefined local variable or method `this_is_not_defined'/) end it "should provide a nice source location" do expect(@exception.source_location).to eq("on line #4") end it "should create a pretty output for the terminal" do expect(@exception.to_s).to match(/Chef::Mixin::Template::TemplateError/) expect(@exception.to_s).to match(/undefined local variable or method `this_is_not_defined'/) expect(@exception.to_s).to include(" 2: bar\n 3: baz\n 4: <%= this_is_not_defined %>\n 5: quin\n 6: qunx") expect(@exception.to_s).to include(@exception.original_exception.backtrace.first) end end end end chef-12.3.0/spec/unit/chef_fs/0000755000004100000410000000000012520074675016050 5ustar www-datawww-datachef-12.3.0/spec/unit/chef_fs/config_spec.rb0000644000004100000410000000720112520074675020654 0ustar www-datawww-data# # Author:: Jess Mink () # Copyright:: Copyright (c) 2014 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/exceptions' require 'lib/chef/chef_fs/config.rb' describe Chef::ChefFS::Config do describe "initialize" do it "warns when hosted setups use 'everything'" do base_config = Hash.new() base_config[:repo_mode] = 'everything' base_config[:chef_server_url] = 'http://foo.com/organizations/fake_org/' ui = double("ui") expect(ui).to receive(:warn) Chef::ChefFS::Config.new(base_config, Dir.pwd, {}, ui) end it "doesn't warn when hosted setups use 'hosted_everything'" do base_config = Hash.new() base_config[:repo_mode] = 'hosted_everything' base_config[:chef_server_url] = 'http://foo.com/organizations/fake_org/' ui = double("ui") expect(ui).to receive(:warn).exactly(0).times Chef::ChefFS::Config.new(base_config, Dir.pwd, {}, ui) end it "doesn't warn when non-hosted setups use 'everything'" do base_config = Hash.new() base_config[:repo_mode] = 'everything' base_config[:chef_server_url] = 'http://foo.com/' ui = double("ui") expect(ui).to receive(:warn).exactly(0).times Chef::ChefFS::Config.new(base_config, Dir.pwd, {}, ui) end end describe "local FS configuration" do let(:chef_config) do Mash.new({ client_path: "/base_path/clients", cookbook_path: "/base_path/cookbooks", data_bag_path: "/base_path/data_bags", environment_path: "/base_path/environments", node_path: "/base_path/nodes", role_path: "/base_path/roles", user_path: "/base_path/users", policy_path: "/base_path/policies" }) end let(:chef_fs_config) { Chef::ChefFS::Config.new(chef_config, Dir.pwd) } subject(:local_fs) { chef_fs_config.local_fs } def platform_path(*args) File.expand_path(*args) end it "sets the correct nodes path on the local FS object" do expect(local_fs.child_paths["nodes"]).to eq([platform_path("/base_path/nodes")]) end it "sets the correct cookbook path on the local FS object" do expect(local_fs.child_paths["cookbooks"]).to eq([platform_path("/base_path/cookbooks")]) end it "sets the correct data bag path on the local FS object" do expect(local_fs.child_paths["data_bags"]).to eq([platform_path("/base_path/data_bags")]) end it "sets the correct environment path on the local FS object" do expect(local_fs.child_paths["environments"]).to eq([platform_path("/base_path/environments")]) end it "sets the correct role path on the local FS object" do expect(local_fs.child_paths["roles"]).to eq([platform_path("/base_path/roles")]) end it "sets the correct user path on the local FS object" do expect(local_fs.child_paths["users"]).to eq([platform_path("/base_path/users")]) end it "sets the correct policy path on the local FS object" do expect(local_fs.child_paths["policies"]).to eq([platform_path("/base_path/policies")]) end end end chef-12.3.0/spec/unit/chef_fs/file_pattern_spec.rb0000644000004100000410000005322612520074675022073 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_pattern' describe Chef::ChefFS::FilePattern do def p(str) Chef::ChefFS::FilePattern.new(str) end # Different kinds of patterns context 'with empty pattern ""' do let(:pattern) { Chef::ChefFS::FilePattern.new('') } it 'match?' do expect(pattern.match?('')).to be_truthy expect(pattern.match?('/')).to be_falsey expect(pattern.match?('a')).to be_falsey expect(pattern.match?('a/b')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('') end it 'could_match_children?' do expect(pattern.could_match_children?('')).to be_falsey expect(pattern.could_match_children?('a/b')).to be_falsey end end context 'with root pattern "/"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/') } it 'match?' do expect(pattern.match?('/')).to be_truthy expect(pattern.match?('')).to be_falsey expect(pattern.match?('a')).to be_falsey expect(pattern.match?('/a')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('/') end it 'could_match_children?' do expect(pattern.could_match_children?('')).to be_falsey expect(pattern.could_match_children?('/')).to be_falsey expect(pattern.could_match_children?('a')).to be_falsey expect(pattern.could_match_children?('a/b')).to be_falsey expect(pattern.could_match_children?('/a')).to be_falsey end end context 'with simple pattern "abc"' do let(:pattern) { Chef::ChefFS::FilePattern.new('abc') } it 'match?' do expect(pattern.match?('abc')).to be_truthy expect(pattern.match?('a')).to be_falsey expect(pattern.match?('abcd')).to be_falsey expect(pattern.match?('/abc')).to be_falsey expect(pattern.match?('')).to be_falsey expect(pattern.match?('/')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('abc') end it 'could_match_children?' do expect(pattern.could_match_children?('')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc')).to be_falsey end end context 'with simple pattern "/abc"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc') } it 'match?' do expect(pattern.match?('/abc')).to be_truthy expect(pattern.match?('abc')).to be_falsey expect(pattern.match?('a')).to be_falsey expect(pattern.match?('abcd')).to be_falsey expect(pattern.match?('')).to be_falsey expect(pattern.match?('/')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('/abc') end it 'could_match_children?' do expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc')).to be_falsey expect(pattern.could_match_children?('/')).to be_truthy expect(pattern.could_match_children?('')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') end end context 'with simple pattern "abc/def/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('abc/def/ghi') } it 'match?' do expect(pattern.match?('abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/def/ghi')).to be_falsey expect(pattern.match?('abc')).to be_falsey expect(pattern.match?('abc/def')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('abc/def/ghi') end it 'could_match_children?' do expect(pattern.could_match_children?('abc')).to be_truthy expect(pattern.could_match_children?('xyz')).to be_falsey expect(pattern.could_match_children?('/abc')).to be_falsey expect(pattern.could_match_children?('abc/def')).to be_truthy expect(pattern.could_match_children?('abc/xyz')).to be_falsey expect(pattern.could_match_children?('abc/def/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('abc')).to eq('def') expect(pattern.exact_child_name_under('abc/def')).to eq('ghi') end end context 'with simple pattern "/abc/def/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/def/ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('abc/def/ghi')).to be_falsey expect(pattern.match?('/abc')).to be_falsey expect(pattern.match?('/abc/def')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('/abc/def/ghi') end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/xyz')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc/def')).to be_truthy expect(pattern.could_match_children?('/abc/xyz')).to be_falsey expect(pattern.could_match_children?('/abc/def/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq('def') expect(pattern.exact_child_name_under('/abc/def')).to eq('ghi') end end context 'with simple pattern "a\*\b"', :pending => (Chef::Platform.windows?) do let(:pattern) { Chef::ChefFS::FilePattern.new('a\*\b') } it 'match?' do expect(pattern.match?('a*b')).to be_truthy expect(pattern.match?('ab')).to be_falsey expect(pattern.match?('acb')).to be_falsey expect(pattern.match?('ab')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to eq('a*b') end it 'could_match_children?' do expect(pattern.could_match_children?('a/*b')).to be_falsey end end context 'with star pattern "/abc/*/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/*/ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/ghi')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/xyz')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc/def')).to be_truthy expect(pattern.could_match_children?('/abc/xyz')).to be_truthy expect(pattern.could_match_children?('/abc/def/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/def')).to eq('ghi') end end context 'with star pattern "/abc/d*f/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/d*f/ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/dxf/ghi')).to be_truthy expect(pattern.match?('/abc/df/ghi')).to be_truthy expect(pattern.match?('/abc/dxyzf/ghi')).to be_truthy expect(pattern.match?('/abc/d/ghi')).to be_falsey expect(pattern.match?('/abc/f/ghi')).to be_falsey expect(pattern.match?('/abc/ghi')).to be_falsey expect(pattern.match?('/abc/xyz/ghi')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/xyz')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc/def')).to be_truthy expect(pattern.could_match_children?('/abc/xyz')).to be_falsey expect(pattern.could_match_children?('/abc/dxyzf')).to be_truthy expect(pattern.could_match_children?('/abc/df')).to be_truthy expect(pattern.could_match_children?('/abc/d')).to be_falsey expect(pattern.could_match_children?('/abc/f')).to be_falsey expect(pattern.could_match_children?('/abc/def/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/def')).to eq('ghi') end end context 'with star pattern "/abc/d??f/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/d??f/ghi') } it 'match?' do expect(pattern.match?('/abc/deef/ghi')).to be_truthy expect(pattern.match?('/abc/deeef/ghi')).to be_falsey expect(pattern.match?('/abc/def/ghi')).to be_falsey expect(pattern.match?('/abc/df/ghi')).to be_falsey expect(pattern.match?('/abc/d/ghi')).to be_falsey expect(pattern.match?('/abc/f/ghi')).to be_falsey expect(pattern.match?('/abc/ghi')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/xyz')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc/deef')).to be_truthy expect(pattern.could_match_children?('/abc/deeef')).to be_falsey expect(pattern.could_match_children?('/abc/def')).to be_falsey expect(pattern.could_match_children?('/abc/df')).to be_falsey expect(pattern.could_match_children?('/abc/d')).to be_falsey expect(pattern.could_match_children?('/abc/f')).to be_falsey expect(pattern.could_match_children?('/abc/deef/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/deef')).to eq('ghi') end end context 'with star pattern "/abc/d[a-z][0-9]f/ghi"', :pending => (Chef::Platform.windows?) do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/d[a-z][0-9]f/ghi') } it 'match?' do expect(pattern.match?('/abc/de1f/ghi')).to be_truthy expect(pattern.match?('/abc/deef/ghi')).to be_falsey expect(pattern.match?('/abc/d11f/ghi')).to be_falsey expect(pattern.match?('/abc/de11f/ghi')).to be_falsey expect(pattern.match?('/abc/dee1f/ghi')).to be_falsey expect(pattern.match?('/abc/df/ghi')).to be_falsey expect(pattern.match?('/abc/d/ghi')).to be_falsey expect(pattern.match?('/abc/f/ghi')).to be_falsey expect(pattern.match?('/abc/ghi')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/xyz')).to be_falsey expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/abc/de1f')).to be_truthy expect(pattern.could_match_children?('/abc/deef')).to be_falsey expect(pattern.could_match_children?('/abc/d11f')).to be_falsey expect(pattern.could_match_children?('/abc/de11f')).to be_falsey expect(pattern.could_match_children?('/abc/dee1f')).to be_falsey expect(pattern.could_match_children?('/abc/def')).to be_falsey expect(pattern.could_match_children?('/abc/df')).to be_falsey expect(pattern.could_match_children?('/abc/d')).to be_falsey expect(pattern.could_match_children?('/abc/f')).to be_falsey expect(pattern.could_match_children?('/abc/de1f/ghi')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/de1f')).to eq('ghi') end end context 'with star pattern "/abc/**/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/**/ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/d/e/f/ghi')).to be_truthy expect(pattern.match?('/abc/ghi')).to be_falsey expect(pattern.match?('/abcdef/d/ghi')).to be_falsey expect(pattern.match?('/abc/d/defghi')).to be_falsey expect(pattern.match?('/xyz')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/abc/d')).to be_truthy expect(pattern.could_match_children?('/abc/d/e')).to be_truthy expect(pattern.could_match_children?('/abc/d/e/f')).to be_truthy expect(pattern.could_match_children?('/abc/def/ghi')).to be_truthy expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/xyz')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/def')).to eq(nil) end end context 'with star pattern "/abc**/ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc**/ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/d/e/f/ghi')).to be_truthy expect(pattern.match?('/abc/ghi')).to be_truthy expect(pattern.match?('/abcdef/ghi')).to be_truthy expect(pattern.match?('/abc/defghi')).to be_falsey expect(pattern.match?('/xyz')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/abcdef')).to be_truthy expect(pattern.could_match_children?('/abc/d/e')).to be_truthy expect(pattern.could_match_children?('/abc/d/e/f')).to be_truthy expect(pattern.could_match_children?('/abc/def/ghi')).to be_truthy expect(pattern.could_match_children?('abc')).to be_falsey end it 'could_match_children? /abc** returns false for /xyz' do pending 'Make could_match_children? more rigorous' # At the moment, we return false for this, but in the end it would be nice to return true: expect(pattern.could_match_children?('/xyz')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq(nil) expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/def')).to eq(nil) end end context 'with star pattern "/abc/**ghi"' do let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/**ghi') } it 'match?' do expect(pattern.match?('/abc/def/ghi')).to be_truthy expect(pattern.match?('/abc/def/ghi/ghi')).to be_truthy expect(pattern.match?('/abc/def/ghi/jkl')).to be_falsey expect(pattern.match?('/abc/d/e/f/ghi')).to be_truthy expect(pattern.match?('/abc/ghi')).to be_truthy expect(pattern.match?('/abcdef/ghi')).to be_falsey expect(pattern.match?('/abc/defghi')).to be_truthy expect(pattern.match?('/xyz')).to be_falsey end it 'exact_path' do expect(pattern.exact_path).to be_nil end it 'could_match_children?' do expect(pattern.could_match_children?('/abc')).to be_truthy expect(pattern.could_match_children?('/abcdef')).to be_falsey expect(pattern.could_match_children?('/abc/d/e')).to be_truthy expect(pattern.could_match_children?('/abc/d/e/f')).to be_truthy expect(pattern.could_match_children?('/abc/def/ghi')).to be_truthy expect(pattern.could_match_children?('abc')).to be_falsey expect(pattern.could_match_children?('/xyz')).to be_falsey end it 'exact_child_name_under' do expect(pattern.exact_child_name_under('/')).to eq('abc') expect(pattern.exact_child_name_under('/abc')).to eq(nil) expect(pattern.exact_child_name_under('/abc/def')).to eq(nil) end end context 'with star pattern "a**b**c"' do let(:pattern) { Chef::ChefFS::FilePattern.new('a**b**c') } it 'match?' do expect(pattern.match?('axybzwc')).to be_truthy expect(pattern.match?('abc')).to be_truthy expect(pattern.match?('axyzwc')).to be_falsey expect(pattern.match?('ac')).to be_falsey expect(pattern.match?('a/x/y/b/z/w/c')).to be_truthy end it 'exact_path' do expect(pattern.exact_path).to be_nil end end context 'normalization tests' do it 'handles trailing slashes' do expect(p('abc/').normalized_pattern).to eq('abc') expect(p('abc/').exact_path).to eq('abc') expect(p('abc/').match?('abc')).to be_truthy expect(p('//').normalized_pattern).to eq('/') expect(p('//').exact_path).to eq('/') expect(p('//').match?('/')).to be_truthy expect(p('/./').normalized_pattern).to eq('/') expect(p('/./').exact_path).to eq('/') expect(p('/./').match?('/')).to be_truthy end it 'handles multiple slashes' do expect(p('abc//def').normalized_pattern).to eq('abc/def') expect(p('abc//def').exact_path).to eq('abc/def') expect(p('abc//def').match?('abc/def')).to be_truthy expect(p('abc//').normalized_pattern).to eq('abc') expect(p('abc//').exact_path).to eq('abc') expect(p('abc//').match?('abc')).to be_truthy end it 'handles dot' do expect(p('abc/./def').normalized_pattern).to eq('abc/def') expect(p('abc/./def').exact_path).to eq('abc/def') expect(p('abc/./def').match?('abc/def')).to be_truthy expect(p('./abc/def').normalized_pattern).to eq('abc/def') expect(p('./abc/def').exact_path).to eq('abc/def') expect(p('./abc/def').match?('abc/def')).to be_truthy expect(p('/.').normalized_pattern).to eq('/') expect(p('/.').exact_path).to eq('/') expect(p('/.').match?('/')).to be_truthy end it 'handles dot by itself', :pending => "decide what to do with dot by itself" do expect(p('.').normalized_pattern).to eq('.') expect(p('.').exact_path).to eq('.') expect(p('.').match?('.')).to be_truthy expect(p('./').normalized_pattern).to eq('.') expect(p('./').exact_path).to eq('.') expect(p('./').match?('.')).to be_truthy end it 'handles dotdot' do expect(p('abc/../def').normalized_pattern).to eq('def') expect(p('abc/../def').exact_path).to eq('def') expect(p('abc/../def').match?('def')).to be_truthy expect(p('abc/def/../..').normalized_pattern).to eq('') expect(p('abc/def/../..').exact_path).to eq('') expect(p('abc/def/../..').match?('')).to be_truthy expect(p('/*/../def').normalized_pattern).to eq('/def') expect(p('/*/../def').exact_path).to eq('/def') expect(p('/*/../def').match?('/def')).to be_truthy expect(p('/*/*/../def').normalized_pattern).to eq('/*/def') expect(p('/*/*/../def').exact_path).to be_nil expect(p('/*/*/../def').match?('/abc/def')).to be_truthy expect(p('/abc/def/../..').normalized_pattern).to eq('/') expect(p('/abc/def/../..').exact_path).to eq('/') expect(p('/abc/def/../..').match?('/')).to be_truthy expect(p('abc/../../def').normalized_pattern).to eq('../def') expect(p('abc/../../def').exact_path).to eq('../def') expect(p('abc/../../def').match?('../def')).to be_truthy end it 'handles dotdot with double star' do expect(p('abc**/def/../ghi').exact_path).to be_nil expect(p('abc**/def/../ghi').match?('abc/ghi')).to be_truthy expect(p('abc**/def/../ghi').match?('abc/x/y/z/ghi')).to be_truthy expect(p('abc**/def/../ghi').match?('ghi')).to be_falsey end it 'raises error on dotdot with overlapping double star' do expect { Chef::ChefFS::FilePattern.new('abc/**/../def').exact_path }.to raise_error(ArgumentError) expect { Chef::ChefFS::FilePattern.new('abc/**/abc/../../def').exact_path }.to raise_error(ArgumentError) end it 'handles leading dotdot' do expect(p('../abc/def').exact_path).to eq('../abc/def') expect(p('../abc/def').match?('../abc/def')).to be_truthy expect(p('/../abc/def').exact_path).to eq('/abc/def') expect(p('/../abc/def').match?('/abc/def')).to be_truthy expect(p('..').exact_path).to eq('..') expect(p('..').match?('..')).to be_truthy expect(p('/..').exact_path).to eq('/') expect(p('/..').match?('/')).to be_truthy end end # match? # - single element matches (empty, fixed, ?, *, characters, escapes) # - nested matches # - absolute matches # - trailing slashes # - ** # exact_path # - empty # - single element and nested matches, with escapes # - absolute and relative # - ?, *, characters, ** # could_match_children? # # # # context 'with pattern "abc"' do end context 'with pattern "/abc"' do end context 'with pattern "abc/def/ghi"' do end context 'with pattern "/abc/def/ghi"' do end # Exercise the different methods to their maximum end chef-12.3.0/spec/unit/chef_fs/file_system_spec.rb0000644000004100000410000001062612520074675021737 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system' require 'chef/chef_fs/file_pattern' describe Chef::ChefFS::FileSystem do include FileSystemSupport context 'with empty filesystem' do let(:fs) { memory_fs('', {}) } context 'list' do it '/' do list_should_yield_paths(fs, '/', '/') end it '/a' do list_should_yield_paths(fs, '/a', '/a') end it '/a/b' do list_should_yield_paths(fs, '/a/b', '/a/b') end it '/*' do list_should_yield_paths(fs, '/*', '/') end end context 'resolve_path' do it '/' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/').path).to eq('/') end it 'nonexistent /a' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/a').path).to eq('/a') end it 'nonexistent /a/b' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/a/b').path).to eq('/a/b') end end end context 'with a populated filesystem' do let(:fs) { memory_fs('', { :a => { :aa => { :c => '', :zz => '' }, :ab => { :c => '', } }, :x => '', :y => {} }) } context 'list' do it '/**' do list_should_yield_paths(fs, '/**', '/', '/a', '/x', '/y', '/a/aa', '/a/aa/c', '/a/aa/zz', '/a/ab', '/a/ab/c') end it '/' do list_should_yield_paths(fs, '/', '/') end it '/*' do list_should_yield_paths(fs, '/*', '/', '/a', '/x', '/y') end it '/*/*' do list_should_yield_paths(fs, '/*/*', '/a/aa', '/a/ab') end it '/*/*/*' do list_should_yield_paths(fs, '/*/*/*', '/a/aa/c', '/a/aa/zz', '/a/ab/c') end it '/*/*/?' do list_should_yield_paths(fs, '/*/*/?', '/a/aa/c', '/a/ab/c') end it '/a/*/c' do list_should_yield_paths(fs, '/a/*/c', '/a/aa/c', '/a/ab/c') end it '/**b/c' do list_should_yield_paths(fs, '/**b/c', '/a/ab/c') end it '/a/ab/c' do no_blocking_calls_allowed list_should_yield_paths(fs, '/a/ab/c', '/a/ab/c') end it 'nonexistent /a/ab/blah' do no_blocking_calls_allowed list_should_yield_paths(fs, '/a/ab/blah', '/a/ab/blah') end it 'nonexistent /a/ab/blah/bjork' do no_blocking_calls_allowed list_should_yield_paths(fs, '/a/ab/blah/bjork', '/a/ab/blah/bjork') end end context 'resolve_path' do before(:each) do no_blocking_calls_allowed end it 'resolves /' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/').path).to eq('/') end it 'resolves /x' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/x').path).to eq('/x') end it 'resolves /a' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/a').path).to eq('/a') end it 'resolves /a/aa' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/a/aa').path).to eq('/a/aa') end it 'resolves /a/aa/zz' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/a/aa/zz').path).to eq('/a/aa/zz') end it 'resolves nonexistent /q/x/w' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/q/x/w').path).to eq('/q/x/w') end end context 'empty?' do it 'is not empty /' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/').empty?).to be false end it 'is empty /y' do expect(Chef::ChefFS::FileSystem.resolve_path(fs, '/y').empty?).to be true end it 'is not a directory and can\'t be tested /x' do expect { Chef::ChefFS::FileSystem.resolve_path(fs, '/x').empty? }.to raise_error(NoMethodError) end end end end chef-12.3.0/spec/unit/chef_fs/diff_spec.rb0000644000004100000410000002733612520074675020332 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_pattern' require 'chef/chef_fs/command_line' # Removes the date stamp from the diff and replaces it with ' DATE' # example match: "/dev/null\t2012-10-16 16:15:54.000000000 +0000" # windows match: "--- /dev/null\tTue Oct 16 18:04:34 2012" def remove_os_differences(diff) diff = diff.gsub(/([+-]{3}.*)\t.*/, '\1 DATE') diff.gsub(/^@@ -\d(,\d)? \+\d(,\d)? @@/, 'CONTEXT_LINE_NUMBERS') end describe 'diff', :uses_diff => true do include FileSystemSupport context 'with two filesystems with all types of difference' do let(:a) { memory_fs('a', { :both_dirs => { :sub_both_dirs => { :subsub => nil }, :sub_both_files => nil, :sub_both_files_different => "a\n", :sub_both_dirs_empty => {}, :sub_dirs_empty_in_a_filled_in_b => {}, :sub_dirs_empty_in_b_filled_in_a => { :subsub => nil }, :sub_a_only_dir => { :subsub => nil }, :sub_a_only_file => nil, :sub_dir_in_a_file_in_b => {}, :sub_file_in_a_dir_in_b => nil }, :both_files => nil, :both_files_different => "a\n", :both_dirs_empty => {}, :dirs_empty_in_a_filled_in_b => {}, :dirs_empty_in_b_filled_in_a => { :subsub => nil }, :dirs_in_a_cannot_be_in_b => {}, :file_in_a_cannot_be_in_b => nil, :a_only_dir => { :subsub => nil }, :a_only_file => nil, :dir_in_a_file_in_b => {}, :file_in_a_dir_in_b => nil }, /cannot_be_in_a/) } let(:b) { memory_fs('b', { :both_dirs => { :sub_both_dirs => { :subsub => nil }, :sub_both_files => nil, :sub_both_files_different => "b\n", :sub_both_dirs_empty => {}, :sub_dirs_empty_in_a_filled_in_b => { :subsub => nil }, :sub_dirs_empty_in_b_filled_in_a => {}, :sub_b_only_dir => { :subsub => nil }, :sub_b_only_file => nil, :sub_dir_in_a_file_in_b => nil, :sub_file_in_a_dir_in_b => {} }, :both_files => nil, :both_files_different => "b\n", :both_dirs_empty => {}, :dirs_empty_in_a_filled_in_b => { :subsub => nil }, :dirs_empty_in_b_filled_in_a => {}, :dirs_in_b_cannot_be_in_a => {}, :file_in_b_cannot_be_in_a => nil, :b_only_dir => { :subsub => nil }, :b_only_file => nil, :dir_in_a_file_in_b => nil, :file_in_a_dir_in_b => {} }, /cannot_be_in_b/) } it 'Chef::ChefFS::CommandLine.diff_print(/)' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/'), a, b, nil, nil) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ 'diff --knife a/both_dirs/sub_both_files_different b/both_dirs/sub_both_files_different --- a/both_dirs/sub_both_files_different DATE +++ b/both_dirs/sub_both_files_different DATE CONTEXT_LINE_NUMBERS -a +b ','diff --knife a/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub new file --- /dev/null DATE +++ b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub DATE CONTEXT_LINE_NUMBERS +subsub ','diff --knife a/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub b/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub deleted file --- a/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -subsub ','Only in a/both_dirs: sub_a_only_dir ','diff --knife a/both_dirs/sub_a_only_file b/both_dirs/sub_a_only_file deleted file --- a/both_dirs/sub_a_only_file DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -sub_a_only_file ','File a/both_dirs/sub_dir_in_a_file_in_b is a directory while file b/both_dirs/sub_dir_in_a_file_in_b is a regular file ','File a/both_dirs/sub_file_in_a_dir_in_b is a regular file while file b/both_dirs/sub_file_in_a_dir_in_b is a directory ','Only in b/both_dirs: sub_b_only_dir ','diff --knife a/both_dirs/sub_b_only_file b/both_dirs/sub_b_only_file new file --- /dev/null DATE +++ b/both_dirs/sub_b_only_file DATE CONTEXT_LINE_NUMBERS +sub_b_only_file ','diff --knife a/both_files_different b/both_files_different --- a/both_files_different DATE +++ b/both_files_different DATE CONTEXT_LINE_NUMBERS -a +b ','diff --knife a/dirs_empty_in_a_filled_in_b/subsub b/dirs_empty_in_a_filled_in_b/subsub new file --- /dev/null DATE +++ b/dirs_empty_in_a_filled_in_b/subsub DATE CONTEXT_LINE_NUMBERS +subsub ','diff --knife a/dirs_empty_in_b_filled_in_a/subsub b/dirs_empty_in_b_filled_in_a/subsub deleted file --- a/dirs_empty_in_b_filled_in_a/subsub DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -subsub ','Only in a: a_only_dir ','diff --knife a/a_only_file b/a_only_file deleted file --- a/a_only_file DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -a_only_file ','File a/dir_in_a_file_in_b is a directory while file b/dir_in_a_file_in_b is a regular file ','File a/file_in_a_dir_in_b is a regular file while file b/file_in_a_dir_in_b is a directory ','Only in b: b_only_dir ','diff --knife a/b_only_file b/b_only_file new file --- /dev/null DATE +++ b/b_only_file DATE CONTEXT_LINE_NUMBERS +b_only_file ' ]) end it 'Chef::ChefFS::CommandLine.diff_print(/both_dirs)' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/both_dirs'), a, b, nil, nil) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ 'diff --knife a/both_dirs/sub_both_files_different b/both_dirs/sub_both_files_different --- a/both_dirs/sub_both_files_different DATE +++ b/both_dirs/sub_both_files_different DATE CONTEXT_LINE_NUMBERS -a +b ','diff --knife a/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub new file --- /dev/null DATE +++ b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub DATE CONTEXT_LINE_NUMBERS +subsub ','diff --knife a/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub b/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub deleted file --- a/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -subsub ','Only in a/both_dirs: sub_a_only_dir ','diff --knife a/both_dirs/sub_a_only_file b/both_dirs/sub_a_only_file deleted file --- a/both_dirs/sub_a_only_file DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -sub_a_only_file ','File a/both_dirs/sub_dir_in_a_file_in_b is a directory while file b/both_dirs/sub_dir_in_a_file_in_b is a regular file ','File a/both_dirs/sub_file_in_a_dir_in_b is a regular file while file b/both_dirs/sub_file_in_a_dir_in_b is a directory ','Only in b/both_dirs: sub_b_only_dir ','diff --knife a/both_dirs/sub_b_only_file b/both_dirs/sub_b_only_file new file --- /dev/null DATE +++ b/both_dirs/sub_b_only_file DATE CONTEXT_LINE_NUMBERS +sub_b_only_file ' ]) end it 'Chef::ChefFS::CommandLine.diff_print(/) with depth 1' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/'), a, b, 1, nil) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ 'Common subdirectories: b/both_dirs ','diff --knife a/both_files_different b/both_files_different --- a/both_files_different DATE +++ b/both_files_different DATE CONTEXT_LINE_NUMBERS -a +b ','Common subdirectories: b/both_dirs_empty ','Common subdirectories: b/dirs_empty_in_b_filled_in_a ','Common subdirectories: b/dirs_empty_in_a_filled_in_b ','Only in a: a_only_dir ','diff --knife a/a_only_file b/a_only_file deleted file --- a/a_only_file DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -a_only_file ','File a/dir_in_a_file_in_b is a directory while file b/dir_in_a_file_in_b is a regular file ','File a/file_in_a_dir_in_b is a regular file while file b/file_in_a_dir_in_b is a directory ','Only in b: b_only_dir ','diff --knife a/b_only_file b/b_only_file new file --- /dev/null DATE +++ b/b_only_file DATE CONTEXT_LINE_NUMBERS +b_only_file ' ]) end it 'Chef::ChefFS::CommandLine.diff_print(/*_*) with depth 0' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/*_*'), a, b, 0, nil) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ 'Common subdirectories: b/both_dirs ','diff --knife a/both_files_different b/both_files_different --- a/both_files_different DATE +++ b/both_files_different DATE CONTEXT_LINE_NUMBERS -a +b ','Common subdirectories: b/both_dirs_empty ','Common subdirectories: b/dirs_empty_in_b_filled_in_a ','Common subdirectories: b/dirs_empty_in_a_filled_in_b ','Only in a: a_only_dir ','diff --knife a/a_only_file b/a_only_file deleted file --- a/a_only_file DATE +++ /dev/null DATE CONTEXT_LINE_NUMBERS -a_only_file ','File a/dir_in_a_file_in_b is a directory while file b/dir_in_a_file_in_b is a regular file ','File a/file_in_a_dir_in_b is a regular file while file b/file_in_a_dir_in_b is a directory ','Only in b: b_only_dir ','diff --knife a/b_only_file b/b_only_file new file --- /dev/null DATE +++ b/b_only_file DATE CONTEXT_LINE_NUMBERS +b_only_file ' ]) end it 'Chef::ChefFS::CommandLine.diff_print(/) in name-only mode' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/'), a, b, nil, :name_only) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ "b/both_dirs/sub_both_files_different\n", "b/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub\n", "b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub\n", "b/both_dirs/sub_a_only_dir\n", "b/both_dirs/sub_a_only_file\n", "b/both_dirs/sub_b_only_dir\n", "b/both_dirs/sub_b_only_file\n", "b/both_dirs/sub_dir_in_a_file_in_b\n", "b/both_dirs/sub_file_in_a_dir_in_b\n", "b/both_files_different\n", "b/dirs_empty_in_b_filled_in_a/subsub\n", "b/dirs_empty_in_a_filled_in_b/subsub\n", "b/a_only_dir\n", "b/a_only_file\n", "b/b_only_dir\n", "b/b_only_file\n", "b/dir_in_a_file_in_b\n", "b/file_in_a_dir_in_b\n" ]) end it 'Chef::ChefFS::CommandLine.diff_print(/) in name-status mode' do results = [] Chef::ChefFS::CommandLine.diff_print(pattern('/'), a, b, nil, :name_status) do |diff| results << remove_os_differences(diff) end expect(results).to match_array([ "M\tb/both_dirs/sub_both_files_different\n", "D\tb/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub\n", "A\tb/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub\n", "D\tb/both_dirs/sub_a_only_dir\n", "D\tb/both_dirs/sub_a_only_file\n", "A\tb/both_dirs/sub_b_only_dir\n", "A\tb/both_dirs/sub_b_only_file\n", "T\tb/both_dirs/sub_dir_in_a_file_in_b\n", "T\tb/both_dirs/sub_file_in_a_dir_in_b\n", "M\tb/both_files_different\n", "D\tb/dirs_empty_in_b_filled_in_a/subsub\n", "A\tb/dirs_empty_in_a_filled_in_b/subsub\n", "D\tb/a_only_dir\n", "D\tb/a_only_file\n", "A\tb/b_only_dir\n", "A\tb/b_only_file\n", "T\tb/dir_in_a_file_in_b\n", "T\tb/file_in_a_dir_in_b\n" ]) end end end chef-12.3.0/spec/unit/chef_fs/file_system/0000755000004100000410000000000012520074675020373 5ustar www-datawww-datachef-12.3.0/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb0000644000004100000410000000361412520074675026453 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/operation_failed_error' describe Chef::ChefFS::FileSystem::OperationFailedError do context 'message' do let(:error_message) { 'HTTP error writing: 400 "Bad Request"' } context 'has a cause attribute and HTTP result code is 400' do it 'include error cause' do allow_message_expectations_on_nil response_body = '{"error":["Invalid key test in request body"]}' allow(@response).to receive(:code).and_return("400") allow(@response).to receive(:body).and_return(response_body) exception = Net::HTTPServerException.new("(exception) unauthorized", @response) expect { raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, exception), error_message }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}") end end context 'does not have a cause attribute' do it 'does not include error cause' do expect { raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self), error_message }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, error_message) end end end end chef-12.3.0/spec/unit/chef_fs/parallelizer.rb0000644000004100000410000003630112520074675021066 0ustar www-datawww-datarequire 'spec_helper' require 'chef/chef_fs/parallelizer' describe Chef::ChefFS::Parallelizer do before :each do @start_time = Time.now end def elapsed_time Time.now - @start_time end after :each do parallelizer.kill end context 'With a Parallelizer with 5 threads' do let :parallelizer do Chef::ChefFS::Parallelizer.new(5) end def parallelize(inputs, options = {}, &block) parallelizer.parallelize(inputs, { :main_thread_processing => false }.merge(options), &block) end it "parallel_do creates unordered output as soon as it is available" do outputs = [] parallelizer.parallel_do([0.5,0.3,0.1]) do |val| sleep val outputs << val end expect(elapsed_time).to be < 0.6 expect(outputs).to eq([ 0.1, 0.3, 0.5 ]) end context "With :ordered => false (unordered output)" do it "An empty input produces an empty output" do parallelize([], :ordered => false) do sleep 10 end.to_a == [] expect(elapsed_time).to be < 0.1 end it "10 sleep(0.2)s complete within 0.5 seconds" do expect(parallelize(1.upto(10), :ordered => false) do |i| sleep 0.2 'x' end.to_a).to eq(%w(x x x x x x x x x x)) expect(elapsed_time).to be < 0.5 end it "The output comes as soon as it is available" do enum = parallelize([0.5,0.3,0.1], :ordered => false) do |val| sleep val val end expect(enum.map do |value| expect(elapsed_time).to be < value+0.1 value end).to eq([ 0.1, 0.3, 0.5 ]) end it "An exception in input is passed through but does NOT stop processing" do input = TestEnumerable.new(0.5,0.3,0.1) do raise 'hi' end enum = parallelize(input, :ordered => false) { |x| sleep(x); x } results = [] expect { enum.each { |value| results << value } }.to raise_error 'hi' expect(results).to eq([ 0.1, 0.3, 0.5 ]) expect(elapsed_time).to be < 0.6 end it "Exceptions in output are raised after all processing is done" do processed = 0 enum = parallelize([1,2,'x',3], :ordered => false) do |x| if x == 'x' sleep 0.1 raise 'hi' end sleep 0.2 processed += 1 x end results = [] expect { enum.each { |value| results << value } }.to raise_error 'hi' expect(results.sort).to eq([ 1, 2, 3 ]) expect(elapsed_time).to be < 0.3 expect(processed).to eq(3) end it "Exceptions with :stop_on_exception are raised after all processing is done" do processed = 0 parallelized = parallelize([0.3,0.3,'x',0.3,0.3,0.3,0.3,0.3], :ordered => false, :stop_on_exception => true) do |x| if x == 'x' sleep(0.1) raise 'hi' end sleep(x) processed += 1 x end expect { parallelized.to_a }.to raise_error 'hi' expect(processed).to eq(4) end end context "With :ordered => true (ordered output)" do it "An empty input produces an empty output" do parallelize([]) do sleep 10 end.to_a == [] expect(elapsed_time).to be < 0.1 end it "10 sleep(0.2)s complete within 0.5 seconds" do expect(parallelize(1.upto(10), :ordered => true) do |i| sleep 0.2 'x' end.to_a).to eq(%w(x x x x x x x x x x)) expect(elapsed_time).to be < 0.5 end it "Output comes in the order of the input" do enum = parallelize([0.5,0.3,0.1]) do |val| sleep val val end.enum_for(:each_with_index) expect(enum.next).to eq([ 0.5, 0 ]) expect(enum.next).to eq([ 0.3, 1 ]) expect(enum.next).to eq([ 0.1, 2 ]) expect(elapsed_time).to be < 0.6 end it "Exceptions in input are raised in the correct sequence but do NOT stop processing" do input = TestEnumerable.new(0.5,0.3,0.1) do raise 'hi' end results = [] enum = parallelize(input) { |x| sleep(x); x } expect { enum.each { |value| results << value } }.to raise_error 'hi' expect(elapsed_time).to be < 0.6 expect(results).to eq([ 0.5, 0.3, 0.1 ]) end it "Exceptions in output are raised in the correct sequence and running processes do NOT stop processing" do processed = 0 enum = parallelize([1,2,'x',3]) do |x| if x == 'x' sleep(0.1) raise 'hi' end sleep(0.2) processed += 1 x end results = [] expect { enum.each { |value| results << value } }.to raise_error 'hi' expect(results).to eq([ 1, 2 ]) expect(elapsed_time).to be < 0.3 expect(processed).to eq(3) end it "Exceptions with :stop_on_exception are raised after all processing is done" do processed = 0 parallelized = parallelize([0.3,0.3,'x',0.3,0.3,0.3,0.3,0.3], :ordered => false, :stop_on_exception => true) do |x| if x == 'x' sleep(0.1) raise 'hi' end sleep(x) processed += 1 x end expect { parallelized.to_a }.to raise_error 'hi' expect(processed).to eq(4) end end it "When the input is slow, output still proceeds" do input = TestEnumerable.new do |&block| block.call(1) sleep 0.1 block.call(2) sleep 0.1 block.call(3) sleep 0.1 end enum = parallelize(input) { |x| x } expect(enum.map do |value| expect(elapsed_time).to be < (value+1)*0.1 value end).to eq([ 1, 2, 3 ]) end end context "With a Parallelizer with 1 thread" do let :parallelizer do Chef::ChefFS::Parallelizer.new(1) end context "when the thread is occupied with a job" do before :each do parallelizer started = false @occupying_job_finished = occupying_job_finished = [ false ] @thread = Thread.new do begin parallelizer.parallelize([0], :main_thread_processing => false) do |x| started = true sleep(0.3) occupying_job_finished[0] = true end.wait ensure end end while !started sleep(0.01) end end after :each do if RUBY_VERSION.to_f > 1.8 Thread.kill(@thread) end end it "parallelize with :main_thread_processing = true does not block" do expect(parallelizer.parallelize([1]) do |x| sleep(0.1) x end.to_a).to eq([ 1 ]) expect(elapsed_time).to be < 0.2 end it "parallelize with :main_thread_processing = false waits for the job to finish" do expect(parallelizer.parallelize([1], :main_thread_processing => false) do |x| sleep(0.1) x+1 end.to_a).to eq([ 2 ]) expect(elapsed_time).to be > 0.3 end it "resizing the Parallelizer to 0 waits for the job to stop" do expect(elapsed_time).to be < 0.2 parallelizer.resize(0) expect(parallelizer.num_threads).to eq(0) expect(elapsed_time).to be > 0.25 expect(@occupying_job_finished).to eq([ true ]) end it "stopping the Parallelizer waits for the job to finish" do expect(elapsed_time).to be < 0.2 parallelizer.stop expect(parallelizer.num_threads).to eq(0) expect(elapsed_time).to be > 0.25 expect(@occupying_job_finished).to eq([ true ]) end it "resizing the Parallelizer to 2 does not stop the job" do expect(elapsed_time).to be < 0.2 parallelizer.resize(2) expect(parallelizer.num_threads).to eq(2) expect(elapsed_time).to be < 0.2 sleep(0.3) expect(@occupying_job_finished).to eq([ true ]) end end context "enumerable methods should run efficiently" do it ".count does not process anything" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.count).to eq(6) expect(outputs_processed).to eq(0) expect(input_mapper.num_processed).to eq(6) end it ".count with arguments works normally" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,1,1,1,2,2,2,3,3,4) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 x end expect(enum.count { |x| x > 1 }).to eq(6) expect(enum.count(2)).to eq(3) expect(outputs_processed).to eq(20) expect(input_mapper.num_processed).to eq(20) end it ".first does not enumerate anything other than the first result(s)" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.first).to eq(1) expect(enum.first(2)).to eq([1,2]) expect(outputs_processed).to eq(3) expect(input_mapper.num_processed).to eq(3) end it ".take does not enumerate anything other than the first result(s)" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.take(2)).to eq([1,2]) expect(outputs_processed).to eq(2) expect(input_mapper.num_processed).to eq(2) end it ".drop does not process anything other than the last result(s)" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.drop(2)).to eq([3,4,5,6]) expect(outputs_processed).to eq(4) expect(input_mapper.num_processed).to eq(6) end if Enumerable.method_defined?(:lazy) it ".lazy.take does not enumerate anything other than the first result(s)" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.lazy.take(2).to_a).to eq([1,2]) expect(outputs_processed).to eq(2) expect(input_mapper.num_processed).to eq(2) end it ".drop does not process anything other than the last result(s)" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end expect(enum.lazy.drop(2).to_a).to eq([3,4,5,6]) expect(outputs_processed).to eq(4) expect(input_mapper.num_processed).to eq(6) end it "lazy enumerable is actually lazy" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3,4,5,6) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 sleep(0.05) # Just enough to yield and get other inputs in the queue x end enum.lazy.take(2) enum.lazy.drop(2) sleep(0.1) expect(outputs_processed).to eq(0) expect(input_mapper.num_processed).to eq(0) end end end context "running enumerable multiple times should function correctly" do it ".map twice on the same parallel enumerable returns the correct results and re-processes the input" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 x end expect(enum.map { |x| x }).to eq([1,2,3]) expect(enum.map { |x| x }).to eq([1,2,3]) expect(outputs_processed).to eq(6) expect(input_mapper.num_processed).to eq(6) end it ".first and then .map on the same parallel enumerable returns the correct results and re-processes the input" do outputs_processed = 0 input_mapper = TestEnumerable.new(1,2,3) enum = parallelizer.parallelize(input_mapper) do |x| outputs_processed += 1 x end expect(enum.first).to eq(1) expect(enum.map { |x| x }).to eq([1,2,3]) expect(outputs_processed).to be >= 4 expect(input_mapper.num_processed).to be >= 4 end it "two simultaneous enumerations throws an exception" do enum = parallelizer.parallelize([1,2,3]) { |x| x } a = enum.enum_for(:each) a.next expect do b = enum.enum_for(:each) b.next end.to raise_error end end end context "With a Parallelizer with 0 threads" do let :parallelizer do Chef::ChefFS::Parallelizer.new(0) end context "And main_thread_processing on" do it "succeeds in running" do expect(parallelizer.parallelize([0.5]) { |x| x*2 }.to_a).to eq([1]) end end end context "With a Parallelizer with 10 threads" do let :parallelizer do Chef::ChefFS::Parallelizer.new(10) end it "does not have contention issues with large numbers of inputs" do expect(parallelizer.parallelize(1.upto(500)) { |x| x+1 }.to_a).to eq(2.upto(501).to_a) end it "does not have contention issues with large numbers of inputs with ordering off" do expect(parallelizer.parallelize(1.upto(500), :ordered => false) { |x| x+1 }.to_a.sort).to eq(2.upto(501).to_a) end it "does not have contention issues with large numbers of jobs and inputs with ordering off" do parallelizers = 0.upto(99).map do parallelizer.parallelize(1.upto(500)) { |x| x+1 } end outputs = [] threads = 0.upto(99).map do |i| Thread.new { outputs[i] = parallelizers[i].to_a } end threads.each { |thread| thread.join } outputs.each { |output| expect(output.sort).to eq(2.upto(501).to_a) } end end class TestEnumerable include Enumerable def initialize(*values, &block) @values = values @block = block @num_processed = 0 end attr_reader :num_processed def each(&each_block) @values.each do |value| @num_processed += 1 each_block.call(value) end if @block @block.call do |value| @num_processed += 1 each_block.call(value) end end end end end chef-12.3.0/spec/unit/chef_fs/data_handler/0000755000004100000410000000000012520074675020456 5ustar www-datawww-datachef-12.3.0/spec/unit/chef_fs/data_handler/group_handler_spec.rb0000644000004100000410000000323712520074675024653 0ustar www-datawww-data# # Author:: Ryan Cragun () # Copyright:: Copyright (c) 2014 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 'lib/chef/chef_fs/data_handler/group_data_handler' class TestEntry < Mash attr_accessor :name, :org def initialize(name, org) @name = name @org = org end end describe Chef::ChefFS::DataHandler::GroupDataHandler do describe '#normalize_for_post' do let(:entry) do TestEntry.new('workers.json', 'hive') end let(:group) do { 'name' => 'worker_bees', 'clients' => %w(honey sting), 'users' => %w(fizz buzz), 'actors' => %w(honey) } end let(:normalized) do { 'actors' => { 'users' => %w(fizz buzz), 'clients'=> %w(honey sting), 'groups'=> [] }, 'groupname' => 'workers', 'name' => 'worker_bees', 'orgname' => 'hive' } end let(:handler) { described_class.new } it 'normalizes the users, clients and groups into actors' do expect(handler.normalize_for_post(group, entry)).to eq(normalized) end end end chef-12.3.0/spec/unit/run_context_spec.rb0000644000004100000410000001370412520074675020367 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 2010 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/lib/library_load_order' describe Chef::RunContext do let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) } let(:cookbook_collection) { cl = Chef::CookbookLoader.new(chef_repo_path) cl.load_cookbooks Chef::CookbookCollection.new(cl) } let(:node) { node = Chef::Node.new node.run_list << "test" << "test::one" << "test::two" node } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) } before(:each) do @original_log_level = Chef::Log.level Chef::Log.level = :debug end after(:each) do Chef::Log.level = @original_log_level end it "has a cookbook collection" do expect(run_context.cookbook_collection).to eq(cookbook_collection) end it "has a node" do expect(run_context.node).to eq(node) end describe "loading cookbooks for a run list" do before do # Each invocation reloads LWRPs, which triggers constant redefinition # warnings. In real usage this isn't an issue because of fork mode. if Chef::Provider.const_defined?(:TestProvider) Chef::Provider.send(:remove_const, :TestProvider) end node.run_list << "test" << "test::one" << "test::two" expect(node).to receive(:loaded_recipe).with(:test, "default") expect(node).to receive(:loaded_recipe).with(:test, "one") expect(node).to receive(:loaded_recipe).with(:test, "two") run_context.load(node.run_list.expand('_default')) end it "should load all the definitions in the cookbooks for this node" do expect(run_context.definitions).to have_key(:new_cat) expect(run_context.definitions).to have_key(:new_badger) expect(run_context.definitions).to have_key(:new_dog) end it "should load all the recipes specified for this node" do expect(run_context.resource_collection[0].to_s).to eq("cat[einstein]") expect(run_context.resource_collection[1].to_s).to eq("cat[loulou]") expect(run_context.resource_collection[2].to_s).to eq("cat[birthday]") expect(run_context.resource_collection[3].to_s).to eq("cat[peanut]") expect(run_context.resource_collection[4].to_s).to eq("cat[fat peanut]") end it "loads all the attribute files in the cookbook collection" do expect(run_context.loaded_fully_qualified_attribute?("test", "george")).to be_truthy expect(node[:george]).to eq("washington") end it "registers attributes files as loaded so they won't be reloaded" do # This test unfortunately is pretty tightly intertwined with the # implementation of how nodes load attribute files, but is the only # convenient way to test this behavior. expect(node).not_to receive(:from_file) node.include_attribute("test::george") end it "raises an error when attempting to include_recipe from a cookbook not reachable by run list or dependencies" do expect(node).to receive(:loaded_recipe).with(:ancient, "aliens") expect do run_context.include_recipe("ancient::aliens") # In CHEF-5120, this becomes a Chef::Exceptions::MissingCookbookDependency error: end.to raise_error(Chef::Exceptions::CookbookNotFound) end it "raises an error on a recipe with a leading :: with no current_cookbook" do expect do run_context.include_recipe("::aliens") end.to raise_error(RuntimeError) end end describe "querying the contents of cookbooks" do let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) } let(:node) { node = Chef::Node.new node.set[:platform] = "ubuntu" node.set[:platform_version] = "13.04" node.name("testing") node } it "queries whether a given cookbook has a specific template" do expect(run_context).to have_template_in_cookbook("openldap", "test.erb") expect(run_context).not_to have_template_in_cookbook("openldap", "missing.erb") end it "errors when querying for a template in a not-available cookbook" do expect do run_context.has_template_in_cookbook?("no-such-cookbook", "foo.erb") end.to raise_error(Chef::Exceptions::CookbookNotFound) end it "queries whether a given cookbook has a specific cookbook_file" do expect(run_context).to have_cookbook_file_in_cookbook("java", "java.response") expect(run_context).not_to have_cookbook_file_in_cookbook("java", "missing.txt") end it "errors when querying for a cookbook_file in a not-available cookbook" do expect do run_context.has_cookbook_file_in_cookbook?("no-such-cookbook", "foo.txt") end.to raise_error(Chef::Exceptions::CookbookNotFound) end end describe "handling reboot requests" do let(:expected) do { :reason => "spec tests require a reboot" } end it "stores and deletes the reboot request" do run_context.request_reboot(expected) expect(run_context.reboot_info).to eq(expected) expect(run_context.reboot_requested?).to be_truthy run_context.cancel_reboot expect(run_context.reboot_info).to eq({}) expect(run_context.reboot_requested?).to be_falsey end end end chef-12.3.0/spec/unit/platform_spec.rb0000644000004100000410000002441012520074675017637 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe "Chef::Platform supports" do [ :freebsd, :ubuntu, :debian, :centos, :fedora, :suse, :opensuse, :redhat, :oracle, :gentoo, :arch, :solaris, :gcel, :ibm_powerkvm ].each do |platform| it "#{platform}" do expect(Chef::Platform.platforms).to have_key(platform) end end end describe Chef::Platform do context "while testing with fake data" do before :all do @original_platform_map = Chef::Platform.platforms end after :all do || Chef::Platform.platforms = @original_platform_map end before(:each) do Chef::Platform.platforms = { :darwin => { ">= 10.11" => { :file => "new_darwinian" }, "9.2.2" => { :file => "darwinian", :else => "thing" }, :default => { :file => "old school", :snicker => "snack" } }, :mars_volta => { }, :default => { :file => Chef::Provider::File, :pax => "brittania", :cat => "nice" } } @events = Chef::EventDispatch::Dispatcher.new end it "should allow you to look up a platform by name and version, returning the provider map for it" do pmap = Chef::Platform.find("Darwin", "9.2.2") expect(pmap).to be_a_kind_of(Hash) expect(pmap[:file]).to eql("darwinian") end it "should allow you to look up a platform by name and version using \"greater than\" style operators" do pmap = Chef::Platform.find("Darwin", "11.1.0") expect(pmap).to be_a_kind_of(Hash) expect(pmap[:file]).to eql("new_darwinian") end it "should use the default providers for an os if the specific version does not exist" do pmap = Chef::Platform.find("Darwin", "1") expect(pmap).to be_a_kind_of(Hash) expect(pmap[:file]).to eql("old school") end it "should use the default providers if the os doesn't give me a default, but does exist" do pmap = Chef::Platform.find("mars_volta", "1") expect(pmap).to be_a_kind_of(Hash) expect(pmap[:file]).to eql(Chef::Provider::File) end it "should use the default provider if the os does not exist" do pmap = Chef::Platform.find("AIX", "1") expect(pmap).to be_a_kind_of(Hash) expect(pmap[:file]).to eql(Chef::Provider::File) end it "should merge the defaults for an os with the specific version" do pmap = Chef::Platform.find("Darwin", "9.2.2") expect(pmap[:file]).to eql("darwinian") expect(pmap[:snicker]).to eql("snack") end it "should merge the defaults for an os with the universal defaults" do pmap = Chef::Platform.find("Darwin", "9.2.2") expect(pmap[:file]).to eql("darwinian") expect(pmap[:pax]).to eql("brittania") end it "should allow you to look up a provider for a platform directly by symbol" do expect(Chef::Platform.find_provider("Darwin", "9.2.2", :file)).to eql("darwinian") end it "should raise an exception if a provider cannot be found for a resource type" do expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(ArgumentError) end it "should look up a provider for a resource with a Chef::Resource object" do kitty = Chef::Resource::Cat.new("loulou") expect(Chef::Platform.find_provider("Darwin", "9.2.2", kitty)).to eql("nice") end it "should look up a provider with a node and a Chef::Resource object" do kitty = Chef::Resource::Cat.new("loulou") node = Chef::Node.new node.name("Intel") node.automatic_attrs[:platform] = "mac_os_x" node.automatic_attrs[:platform_version] = "9.2.2" expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql("nice") end it "should not throw an exception when the platform version has an unknown format" do expect(Chef::Platform.find_provider(:darwin, "bad-version", :file)).to eql("old school") end it "should prefer an explicit provider" do kitty = Chef::Resource::Cat.new("loulou") allow(kitty).to receive(:provider).and_return(Chef::Provider::File) node = Chef::Node.new node.name("Intel") node.automatic_attrs[:platform] = "mac_os_x" node.automatic_attrs[:platform_version] = "9.2.2" expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql(Chef::Provider::File) end it "should look up a provider based on the resource name if nothing else matches" do kitty = Chef::Resource::Cat.new("loulou") class Chef::Provider::Cat < Chef::Provider; end Chef::Platform.platforms[:default].delete(:cat) node = Chef::Node.new node.name("Intel") node.automatic_attrs[:platform] = "mac_os_x" node.automatic_attrs[:platform_version] = "8.5" expect(Chef::Platform.find_provider_for_node(node, kitty)).to eql(Chef::Provider::Cat) end def setup_file_resource node = Chef::Node.new node.automatic_attrs[:platform] = "mac_os_x" node.automatic_attrs[:platform_version] = "9.2.2" run_context = Chef::RunContext.new(node, {}, @events) [ Chef::Resource::File.new("whateva", run_context), run_context ] end it "returns a provider object given a Chef::Resource object which has a valid run context and an action" do file, run_context = setup_file_resource provider = Chef::Platform.provider_for_resource(file, :foo) expect(provider).to be_an_instance_of(Chef::Provider::File) expect(provider.new_resource).to equal(file) expect(provider.run_context).to equal(run_context) end it "returns a provider object given a Chef::Resource object which has a valid run context without an action" do file, run_context = setup_file_resource provider = Chef::Platform.provider_for_resource(file) expect(provider).to be_an_instance_of(Chef::Provider::File) expect(provider.new_resource).to equal(file) expect(provider.run_context).to equal(run_context) end it "raises an error when trying to find the provider for a resource with no run context" do file = Chef::Resource::File.new("whateva") expect {Chef::Platform.provider_for_resource(file)}.to raise_error(ArgumentError) end it "does not support finding a provider by resource and node -- a run context is required" do expect {Chef::Platform.provider_for_node('node', 'resource')}.to raise_error(NotImplementedError) end it "should update the provider map with map" do Chef::Platform.set( :platform => :darwin, :version => "9.2.2", :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:darwin]["9.2.2"][:file]).to eql("masterful") Chef::Platform.set( :platform => :darwin, :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:darwin][:default][:file]).to eql("masterful") Chef::Platform.set( :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:default][:file]).to eql("masterful") Chef::Platform.set( :platform => :hero, :version => "9.2.2", :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:hero]["9.2.2"][:file]).to eql("masterful") Chef::Platform.set( :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:default][:file]).to eql("masterful") Chef::Platform.platforms = {} Chef::Platform.set( :resource => :file, :provider => "masterful" ) expect(Chef::Platform.platforms[:default][:file]).to eql("masterful") Chef::Platform.platforms = { :neurosis => {} } Chef::Platform.set(:platform => :neurosis, :resource => :package, :provider => "masterful") expect(Chef::Platform.platforms[:neurosis][:default][:package]).to eql("masterful") end it "does not overwrite the platform map when using :default platform" do Chef::Platform.set( :resource => :file, :platform => :default, :provider => "new school" ) expect(Chef::Platform.platforms[:default][:file]).to eql("new school") expect(Chef::Platform.platforms[:default][:cat]).to eql("nice") end end context "while testing the configured platform data" do it "should use the solaris package provider on Solaris <11" do pmap = Chef::Platform.find("Solaris2", "5.9") expect(pmap[:package]).to eql(Chef::Provider::Package::Solaris) end it "should use the IPS package provider on Solaris 11" do pmap = Chef::Platform.find("Solaris2", "5.11") expect(pmap[:package]).to eql(Chef::Provider::Package::Ips) end it "should use the Redhat service provider on SLES11" do 1.upto(3) do |sp| pmap = Chef::Platform.find("SUSE", "11.#{sp}") expect(pmap[:service]).to eql(Chef::Provider::Service::Redhat) end end it "should use the Systemd service provider on SLES12" do pmap = Chef::Platform.find("SUSE", "12.0") expect(pmap[:service]).to eql(Chef::Provider::Service::Systemd) end it "should use the SUSE group provider on SLES11" do 1.upto(3) do |sp| pmap = Chef::Platform.find("SUSE", "11.#{sp}") expect(pmap[:group]).to eql(Chef::Provider::Group::Suse) end end it "should use the Gpasswd group provider on SLES12" do pmap = Chef::Platform.find("SUSE", "12.0") expect(pmap[:group]).to eql(Chef::Provider::Group::Gpasswd) end end end chef-12.3.0/spec/unit/data_bag_item_spec.rb0000644000004100000410000002441212520074675020555 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/data_bag_item' describe Chef::DataBagItem do let(:data_bag_item) { Chef::DataBagItem.new } describe "initialize" do it "should be a Chef::DataBagItem" do expect(data_bag_item).to be_a_kind_of(Chef::DataBagItem) end end describe "data_bag" do it "should let you set the data_bag to a string" do expect(data_bag_item.data_bag("clowns")).to eq("clowns") end it "should return the current data_bag type" do data_bag_item.data_bag "clowns" expect(data_bag_item.data_bag).to eq("clowns") end it "should not accept spaces" do expect { data_bag_item.data_bag "clown masters" }.to raise_error(ArgumentError) end it "should throw an ArgumentError if you feed it anything but a string" do expect { data_bag_item.data_bag Hash.new }.to raise_error(ArgumentError) end end describe "raw_data" do it "should let you set the raw_data with a hash" do expect { data_bag_item.raw_data = { "id" => "octahedron" } }.not_to raise_error end it "should let you set the raw_data from a mash" do expect { data_bag_item.raw_data = Mash.new({ "id" => "octahedron" }) }.not_to raise_error end it "should raise an exception if you set the raw data without a key" do expect { data_bag_item.raw_data = { "monkey" => "pants" } }.to raise_error(ArgumentError) end it "should raise an exception if you set the raw data to something other than a hash" do expect { data_bag_item.raw_data = "katie rules" }.to raise_error(ArgumentError) end it "should accept alphanum/-/_ for the id" do expect { data_bag_item.raw_data = { "id" => "h1-_" } }.not_to raise_error end it "should accept alphanum.alphanum for the id" do expect { data_bag_item.raw_data = { "id" => "foo.bar" } }.not_to raise_error end it "should accept .alphanum for the id" do expect { data_bag_item.raw_data = { "id" => ".bozo" } }.not_to raise_error end it "should raise an exception if the id contains anything but alphanum/-/_" do expect { data_bag_item.raw_data = { "id" => "!@#" } }.to raise_error(ArgumentError) end it "should return the raw data" do data_bag_item.raw_data = { "id" => "highway_of_emptiness" } expect(data_bag_item.raw_data).to eq({ "id" => "highway_of_emptiness" }) end it "should be a Mash by default" do expect(data_bag_item.raw_data).to be_a_kind_of(Mash) end end describe "object_name" do let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item.data_bag("dreams") data_bag_item.raw_data = { "id" => "the_beatdown" } data_bag_item } it "should return an object name based on the bag name and the raw_data id" do expect(data_bag_item.object_name).to eq("data_bag_item_dreams_the_beatdown") end end describe "class method object_name" do it "should return an object name based based on the bag name and an id" do expect(Chef::DataBagItem.object_name("zen", "master")).to eq("data_bag_item_zen_master") end end describe "when used like a Hash" do let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item.raw_data = { "id" => "journey", "trials" => "been through" } data_bag_item } it "responds to keys" do expect(data_bag_item.keys).to include("id") expect(data_bag_item.keys).to include("trials") end it "supports element reference with []" do expect(data_bag_item["id"]).to eq("journey") end it "implements all the methods of Hash" do methods = [:rehash, :to_hash, :[], :fetch, :[]=, :store, :default, :default=, :default_proc, :index, :size, :length, :empty?, :each_value, :each_key, :each_pair, :each, :keys, :values, :values_at, :delete, :delete_if, :reject!, :clear, :invert, :update, :replace, :merge!, :merge, :has_key?, :has_value?, :key?, :value?] methods.each do |m| expect(data_bag_item).to respond_to(m) end end end describe "to_hash" do let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item.data_bag("still_lost") data_bag_item.raw_data = { "id" => "whoa", "i_know" => "kung_fu" } data_bag_item } let(:to_hash) { data_bag_item.to_hash } it "should return a hash" do expect(to_hash).to be_a_kind_of(Hash) end it "should have the raw_data keys as top level keys" do expect(to_hash["id"]).to eq("whoa") expect(to_hash["i_know"]).to eq("kung_fu") end it "should have the chef_type of data_bag_item" do expect(to_hash["chef_type"]).to eq("data_bag_item") end it "should have the data_bag set" do expect(to_hash["data_bag"]).to eq("still_lost") end end describe "when deserializing from JSON" do let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item.data_bag('mars_volta') data_bag_item.raw_data = { "id" => "octahedron", "snooze" => { "finally" => :world_will } } data_bag_item } let(:deserial) { Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(data_bag_item)) } it "should deserialize to a Chef::DataBagItem object" do expect(deserial).to be_a_kind_of(Chef::DataBagItem) end it "should have a matching 'data_bag' value" do expect(deserial.data_bag).to eq(data_bag_item.data_bag) end it "should have a matching 'id' key" do expect(deserial["id"]).to eq("octahedron") end it "should have a matching 'snooze' key" do expect(deserial["snooze"]).to eq({ "finally" => "world_will" }) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { data_bag_item } end end describe "when converting to a string" do it "converts to a string in the form data_bag_item[ID]" do data_bag_item['id'] = "heart of darkness" expect(data_bag_item.to_s).to eq('data_bag_item[heart of darkness]') end it "inspects as data_bag_item[BAG, ID, RAW_DATA]" do raw_data = {"id" => "heart_of_darkness", "author" => "Conrad"} data_bag_item.raw_data = raw_data data_bag_item.data_bag("books") expect(data_bag_item.inspect).to eq("data_bag_item[\"books\", \"heart_of_darkness\", #{raw_data.inspect}]") end end describe "save" do let(:server) { instance_double(Chef::REST) } let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item['id'] = "heart of darkness" data_bag_item.raw_data = {"id" => "heart_of_darkness", "author" => "Conrad"} data_bag_item.data_bag("books") data_bag_item } before do expect(Chef::REST).to receive(:new).and_return(server) end it "should update the item when it already exists" do expect(server).to receive(:put_rest).with("data/books/heart_of_darkness", data_bag_item) data_bag_item.save end it "should create if the item is not found" do exception = double("404 error", :code => "404") expect(server).to receive(:put_rest).and_raise(Net::HTTPServerException.new("foo", exception)) expect(server).to receive(:post_rest).with("data/books", data_bag_item) data_bag_item.save end describe "when whyrun mode is enabled" do before do Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end it "should not save" do expect(server).not_to receive(:put_rest) expect(server).not_to receive(:post_rest) data_bag_item.data_bag("books") data_bag_item.save end end end describe "destroy" do let(:server) { instance_double(Chef::REST) } let(:data_bag_item) { data_bag_item = Chef::DataBagItem.new data_bag_item.data_bag('a_baggy_bag') data_bag_item.raw_data = { "id" => "some_id" } data_bag_item } it "should set default parameters" do expect(Chef::REST).to receive(:new).and_return(server) expect(server).to receive(:delete_rest).with("data/a_baggy_bag/data_bag_item_a_baggy_bag_some_id") data_bag_item.destroy end end describe "when loading" do before do data_bag_item.raw_data = {"id" => "charlie", "shell" => "zsh", "ssh_keys" => %w{key1 key2}} data_bag_item.data_bag("users") end describe "from an API call" do let(:http_client) { double("Chef::REST") } before do allow(Chef::REST).to receive(:new).and_return(http_client) end it "converts raw data to a data bag item" do expect(http_client).to receive(:get_rest).with("data/users/charlie").and_return(data_bag_item.to_hash) item = Chef::DataBagItem.load(:users, "charlie") expect(item).to be_a_kind_of(Chef::DataBagItem) expect(item).to eq(data_bag_item) end it "does not convert when a DataBagItem is returned from the API call" do expect(http_client).to receive(:get_rest).with("data/users/charlie").and_return(data_bag_item) item = Chef::DataBagItem.load(:users, "charlie") expect(item).to be_a_kind_of(Chef::DataBagItem) expect(item).to equal(data_bag_item) end end describe "in solo mode" do before do Chef::Config[:solo] = true end after do Chef::Config[:solo] = false end it "converts the raw data to a data bag item" do expect(Chef::DataBag).to receive(:load).with('users').and_return({'charlie' => data_bag_item.to_hash}) item = Chef::DataBagItem.load('users', 'charlie') expect(item).to be_a_kind_of(Chef::DataBagItem) expect(item).to eq(data_bag_item) end end end end chef-12.3.0/spec/unit/shell_out_spec.rb0000644000004100000410000000144412520074675020013 0ustar www-datawww-datarequire File.expand_path('../../spec_helper', __FILE__) describe "Chef::ShellOut deprecation notices" do it "logs a warning when initializing a new Chef::ShellOut object" do expect(Chef::Log).to receive(:warn).with("Chef::ShellOut is deprecated, please use Mixlib::ShellOut") expect(Chef::Log).to receive(:warn).with(/Called from\:/) Chef::ShellOut.new("pwd") end end describe "Chef::Exceptions::ShellCommandFailed deprecation notices" do it "logs a warning when referencing the constant Chef::Exceptions::ShellCommandFailed" do expect(Chef::Log).to receive(:warn).with("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed") expect(Chef::Log).to receive(:warn).with(/Called from\:/) Chef::Exceptions::ShellCommandFailed end end chef-12.3.0/spec/unit/cookbook_uploader_spec.rb0000644000004100000410000001325612520074675021522 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::CookbookUploader do let(:http_client) { double("Chef::REST") } let(:cookbook_loader) do loader = Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks")) loader.load_cookbooks loader.cookbooks_by_name["apache2"].identifier = apache2_identifier loader.cookbooks_by_name["java"].identifier = java_identifier loader end let(:apache2_identifier) { "6644e6cb2ade90b8aff2ebb44728958fbc939ebf" } let(:apache2_cookbook) { cookbook_loader.cookbooks_by_name["apache2"] } let(:java_identifier) { "edd40c30c4e0ebb3658abde4620597597d2e9c17" } let(:java_cookbook) { cookbook_loader.cookbooks_by_name["java"] } let(:cookbooks_to_upload) { [apache2_cookbook, java_cookbook] } let(:checksums_of_cookbook_files) { apache2_cookbook.checksums.merge(java_cookbook.checksums) } let(:checksums_set) do checksums_of_cookbook_files.keys.inject({}) do |set, cksum| set[cksum] = nil set end end let(:sandbox_commit_uri) { "https://chef.example.org/sandboxes/abc123" } let(:policy_mode) { false } let(:uploader) { described_class.new(cookbooks_to_upload, rest: http_client, policy_mode: policy_mode) } it "defaults to not enabling policy mode" do expect(described_class.new(cookbooks_to_upload, rest: http_client).policy_mode?).to be(false) end it "has a list of cookbooks to upload" do expect(uploader.cookbooks).to eq(cookbooks_to_upload) end it "creates an HTTP client with default configuration when not initialized with one" do default_http_client = double("Chef::REST") expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(default_http_client) uploader = described_class.new(cookbooks_to_upload) expect(uploader.rest).to eq(default_http_client) end describe "uploading cookbooks" do def url_for(cksum) "https://storage.example.com/#{cksum}" end let(:sandbox_response) do sandbox_checksums = cksums_not_on_remote.inject({}) do |cksum_map, cksum| cksum_map[cksum] = { "needs_upload" => true, "url" => url_for(cksum)} cksum_map end { "checksums" => sandbox_checksums, "uri" => sandbox_commit_uri } end def expect_sandbox_create expect(http_client).to receive(:post). with("sandboxes", {:checksums => checksums_set}). and_return(sandbox_response) end def expect_checksum_upload checksums_of_cookbook_files.each do |md5, file_path| next unless cksums_not_on_remote.include?(md5) upload_headers = { "content-type" => "application/x-binary", "content-md5" => an_instance_of(String), "accept" => "application/json" } expect(http_client).to receive(:put). with(url_for(md5), IO.binread(file_path), upload_headers) end end def expected_save_url(cookbook) "cookbooks/#{cookbook.name}/#{cookbook.version}" end def expect_sandbox_commit expect(http_client).to receive(:put).with(sandbox_commit_uri, {:is_completed => true}) end def expect_cookbook_create cookbooks_to_upload.each do |cookbook| expect(http_client).to receive(:put). with(expected_save_url(cookbook), cookbook) end end context "when no files exist on the server" do let(:cksums_not_on_remote) do checksums_of_cookbook_files.keys end it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do expect_sandbox_create expect_checksum_upload expect_sandbox_commit expect_cookbook_create uploader.upload_cookbooks end end context "when some files exist on the server" do let(:cksums_not_on_remote) do checksums_of_cookbook_files.keys[0, 1] end it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do expect_sandbox_create expect_checksum_upload expect_sandbox_commit expect_cookbook_create uploader.upload_cookbooks end end context "when all files already exist on the server" do let(:cksums_not_on_remote) { [] } it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do expect_sandbox_create expect_checksum_upload expect_sandbox_commit expect_cookbook_create uploader.upload_cookbooks end end context "when policy_mode is specified" do let(:cksums_not_on_remote) do checksums_of_cookbook_files.keys end let(:policy_mode) { true } def expected_save_url(cookbook) "cookbook_artifacts/#{cookbook.name}/#{cookbook.identifier}" end it "uploads all files in a sandbox transaction, then creates cookbooks on the server using cookbook_artifacts API" do expect_sandbox_create expect_checksum_upload expect_sandbox_commit expect_cookbook_create uploader.upload_cookbooks end end end end chef-12.3.0/spec/unit/chef_class_spec.rb0000644000004100000410000000577212520074675020117 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 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 class" do let(:platform) { "debian" } let(:node) do node = Chef::Node.new node.automatic['platform'] = platform node end let(:run_context) do Chef::RunContext.new(node, nil, nil) end let(:resource_priority_map) do double("Chef::Platform::ResourcePriorityMap") end let(:provider_priority_map) do double("Chef::Platform::ProviderPriorityMap") end before do Chef.set_run_context(run_context) Chef.set_node(node) Chef.set_resource_priority_map(resource_priority_map) Chef.set_provider_priority_map(provider_priority_map) end after do Chef.reset! end context "priority maps" do context "#get_provider_priority_array" do it "should use the current node to get the right priority_map" do expect(provider_priority_map).to receive(:get_priority_array).with(node, :http_request).and_return("stuff") expect(Chef.get_provider_priority_array(:http_request)).to eql("stuff") end end context "#get_resource_priority_array" do it "should use the current node to get the right priority_map" do expect(resource_priority_map).to receive(:get_priority_array).with(node, :http_request).and_return("stuff") expect(Chef.get_resource_priority_array(:http_request)).to eql("stuff") end end context "#set_provider_priority_array" do it "should delegate to the provider_priority_map" do expect(provider_priority_map).to receive(:set_priority_array).with(:http_request, ["a", "b"], platform: "debian").and_return("stuff") expect(Chef.set_provider_priority_array(:http_request, ["a", "b"], platform: "debian")).to eql("stuff") end end context "#set_priority_map_for_resource" do it "should delegate to the resource_priority_map" do expect(resource_priority_map).to receive(:set_priority_array).with(:http_request, ["a", "b"], platform: "debian").and_return("stuff") expect(Chef.set_resource_priority_array(:http_request, ["a", "b"], platform: "debian")).to eql("stuff") end end end context "#run_context" do it "should return the injected RunContext" do expect(Chef.run_context).to eql(run_context) end end context "#node" do it "should return the injected Node" do expect(Chef.node).to eql(node) end end end chef-12.3.0/spec/unit/rest_spec.rb0000644000004100000410000007442012520074675016776 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2010 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 'uri' require 'net/https' require 'stringio' SIGNING_KEY_DOT_PEM="-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh 8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD 2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/ /RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4 DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8 OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12 2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ== -----END RSA PRIVATE KEY-----" describe Chef::REST do let(:base_url) { "http://chef.example.com:4000" } let(:monkey_uri) { URI.parse("http://chef.example.com:4000/monkey") } let(:log_stringio) { StringIO.new } let(:request_id) {"1234"} let(:rest) do allow(Chef::REST::CookieJar).to receive(:instance).and_return({}) allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) Chef::REST::CookieJar.instance.clear rest end let(:standard_read_headers) {{"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}} let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}} before(:each) do Chef::Log.init(log_stringio) end it "should have content length validation middleware after compressor middleware" do middlewares = rest.instance_variable_get(:@middlewares) content_length = middlewares.find_index { |e| e.is_a? Chef::HTTP::ValidateContentLength } decompressor = middlewares.find_index { |e| e.is_a? Chef::HTTP::Decompressor } expect(content_length).not_to be_nil expect(decompressor).not_to be_nil expect(decompressor < content_length).to be_truthy end it "should allow the options hash to be frozen" do options = {}.freeze # should not raise any exception Chef::REST.new(base_url, nil, nil, options) end context 'when created with a chef zero URL' do let(:url) { "chefzero://localhost:1" } it "does not load the signing key" do expect { Chef::REST.new(url) }.to_not raise_error end end describe "calling an HTTP verb on a path or absolute URL" do it "adds a relative URL to the base url it was initialized with" do expect(rest.create_url("foo/bar/baz")).to eq(URI.parse(base_url + "/foo/bar/baz")) end it "replaces the base URL when given an absolute URL" do expect(rest.create_url("http://chef-rulez.example.com:9000")).to eq(URI.parse("http://chef-rulez.example.com:9000")) end it "makes a :GET request with the composed url object" do expect(rest).to receive(:send_http_request). with(:GET, monkey_uri, standard_read_headers, false). and_return([1,2,3]) expect(rest).to receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.get_rest("monkey") end it "makes a :GET reqest for a streaming download with the composed url" do expect(rest).to receive(:streaming_request).with('monkey', {}) rest.get_rest("monkey", true) end it "makes a :DELETE request with the composed url object" do expect(rest).to receive(:send_http_request). with(:DELETE, monkey_uri, standard_read_headers, false). and_return([1,2,3]) expect(rest).to receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.delete_rest("monkey") end it "makes a :POST request with the composed url object and data" do expect(rest).to receive(:send_http_request). with(:POST, monkey_uri, standard_write_headers, "\"data\""). and_return([1,2,3]) expect(rest).to receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.post_rest("monkey", "data") end it "makes a :PUT request with the composed url object and data" do expect(rest).to receive(:send_http_request). with(:PUT, monkey_uri, standard_write_headers, "\"data\""). and_return([1,2,3]) expect(rest).to receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.put_rest("monkey", "data") end end describe "legacy API" do let(:rest) do Chef::REST.new(base_url) end before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" end it 'responds to raw_http_request as a public method' do expect(rest.public_methods.map(&:to_s)).to include("raw_http_request") end it 'calls the authn middleware' do data = "\"secure data\"" auth_headers = standard_write_headers.merge({"auth_done"=>"yep"}) expect(rest.authenticator).to receive(:handle_request). with(:POST, monkey_uri, standard_write_headers, data). and_return([:POST, monkey_uri, auth_headers, data]) expect(rest).to receive(:send_http_request). with(:POST, monkey_uri, auth_headers, data). and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data) end it 'sets correct authn headers' do data = "\"secure data\"" method, uri, auth_headers, d = rest.authenticator.handle_request(:POST, monkey_uri, standard_write_headers, data) expect(rest).to receive(:send_http_request). with(:POST, monkey_uri, auth_headers, data). and_return([1,2,3]) expect(rest).to receive('success_response?'.to_sym).with(1).and_return(true) rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data) end end describe "when configured to authenticate to the Chef server" do let(:base_url) { URI.parse("http://chef.example.com:4000") } let(:rest) do Chef::REST.new(base_url) end before do Chef::Config[:node_name] = "webmonkey.example.com" Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" end it "configures itself to use the node_name and client_key in the config by default" do expect(rest.client_name).to eq("webmonkey.example.com") expect(rest.signing_key_filename).to eq(CHEF_SPEC_DATA + "/ssl/private_key.pem") end it "provides access to the raw key data" do expect(rest.signing_key).to eq(SIGNING_KEY_DOT_PEM) end it "does not error out when initialized without credentials" do rest = Chef::REST.new(base_url, nil, nil) #should_not raise_error hides the bt from you, so screw it. expect(rest.client_name).to be_nil expect(rest.signing_key).to be_nil end it "indicates that requests should not be signed when it has no credentials" do rest = Chef::REST.new(base_url, nil, nil) expect(rest.sign_requests?).to be_falsey end it "raises PrivateKeyMissing when the key file doesn't exist" do expect {Chef::REST.new(base_url, "client-name", "/dev/null/nothing_here")}.to raise_error(Chef::Exceptions::PrivateKeyMissing) end it "raises InvalidPrivateKey when the key file doesnt' look like a key" do invalid_key_file = CHEF_SPEC_DATA + "/bad-config.rb" expect {Chef::REST.new(base_url, "client-name", invalid_key_file)}.to raise_error(Chef::Exceptions::InvalidPrivateKey) end it "can take private key as a sting :raw_key in options during initializaton" do expect(Chef::REST.new(base_url, "client-name", nil, :raw_key => SIGNING_KEY_DOT_PEM).signing_key).to eq(SIGNING_KEY_DOT_PEM) end it "raises InvalidPrivateKey when the key passed as string :raw_key in options doesnt' look like a key" do expect {Chef::REST.new(base_url, "client-name", nil, :raw_key => "bad key string")}.to raise_error(Chef::Exceptions::InvalidPrivateKey) end end context "when making REST requests" do let(:body) { "ninja" } let(:http_response) do http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req") allow(http_response).to receive(:read_body) allow(http_response).to receive(:body).and_return(body) http_response["Content-Length"] = body.bytesize.to_s http_response end let(:host_header) { "one" } let(:url) { URI.parse("http://one:80/?foo=bar") } let(:base_url) { "http://chef.example.com:4000" } let!(:http_client) do http_client = Net::HTTP.new(url.host, url.port) allow(http_client).to receive(:request).and_yield(http_response).and_return(http_response) http_client end let(:rest) do allow(Net::HTTP).to receive(:new).and_return(http_client) allow(Chef::REST::CookieJar).to receive(:instance).and_return({}) allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) Chef::REST::CookieJar.instance.clear rest end let(:base_headers) do { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, 'X-REMOTE-REQUEST-ID' => request_id } end let (:req_with_body_headers) do base_headers.merge("Content-Type" => "application/json", "Content-Length" => '13') end before(:each) do Chef::Config[:ssl_client_cert] = nil Chef::Config[:ssl_client_key] = nil end describe "as JSON API requests" do let(:request_mock) { {} } let(:base_headers) do #FIXME: huh? { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, 'Host' => host_header, 'X-REMOTE-REQUEST-ID' => request_id } end before do allow(Net::HTTP::Get).to receive(:new).and_return(request_mock) end it "should always include the X-Chef-Version header" do expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock) rest.request(:GET, url, {}) end it "should always include the X-Remote-Request-Id header" do expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock) rest.request(:GET, url, {}) end it "sets the user agent to chef-client" do # XXX: must reset to default b/c knife changes the UA Chef::REST::RESTRequest.user_agent = Chef::REST::RESTRequest::DEFAULT_UA rest.request(:GET, url, {}) expect(request_mock['User-Agent']).to match(/^Chef Client\/#{Chef::VERSION}/) end # CHEF-3140 context "when configured to disable compression" do let(:rest) do allow(Net::HTTP).to receive(:new).and_return(http_client) Chef::REST.new(base_url, nil, nil, :disable_gzip => true) end it "does not accept encoding gzip" do expect(rest.send(:build_headers, :GET, url, {})).not_to have_key("Accept-Encoding") end it "does not decompress a response encoded as gzip" do http_response.add_field("content-encoding", "gzip") request = Net::HTTP::Get.new(url.path) expect(Net::HTTP::Get).to receive(:new).and_return(request) # will raise a Zlib error if incorrect expect(rest.request(:GET, url, {})).to eq("ninja") end end context "when configured with custom http headers" do let(:custom_headers) do { 'X-Custom-ChefSecret' => 'sharpknives', 'X-Custom-RequestPriority' => 'extremely low' } end before(:each) do Chef::Config[:custom_http_headers] = custom_headers end after(:each) do Chef::Config[:custom_http_headers] = nil end it "should set them on the http request" do url_string = an_instance_of(String) header_hash = hash_including(custom_headers) expect(Net::HTTP::Get).to receive(:new).with(url_string, header_hash) rest.request(:GET, url, {}) end end context "when setting cookies" do let(:rest) do allow(Net::HTTP).to receive(:new).and_return(http_client) Chef::REST::CookieJar.instance["#{url.host}:#{url.port}"] = "cookie monster" allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) rest end it "should set the cookie for this request if one exists for the given host:port" do expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers.merge('Cookie' => "cookie monster")).and_return(request_mock) rest.request(:GET, url, {}) end end it "should build a new HTTP GET request" do expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock) rest.request(:GET, url, {}) end it "should build a new HTTP POST request" do request = Net::HTTP::Post.new(url.path) expected_headers = base_headers.merge("Content-Type" => 'application/json', 'Content-Length' => '13') expect(Net::HTTP::Post).to receive(:new).with("/?foo=bar", expected_headers).and_return(request) rest.request(:POST, url, {}, {:one=>:two}) expect(request.body).to eq('{"one":"two"}') end it "should build a new HTTP PUT request" do request = Net::HTTP::Put.new(url.path) expected_headers = base_headers.merge("Content-Type" => 'application/json', 'Content-Length' => '13') expect(Net::HTTP::Put).to receive(:new).with("/?foo=bar",expected_headers).and_return(request) rest.request(:PUT, url, {}, {:one=>:two}) expect(request.body).to eq('{"one":"two"}') end it "should build a new HTTP DELETE request" do expect(Net::HTTP::Delete).to receive(:new).with("/?foo=bar", base_headers).and_return(request_mock) rest.request(:DELETE, url) end it "should raise an error if the method is not GET/PUT/POST/DELETE" do expect { rest.request(:MONKEY, url) }.to raise_error(ArgumentError) end it "returns nil when the response is successful but content-type is not JSON" do expect(rest.request(:GET, url)).to eq("ninja") end it "should fail if the response is truncated" do http_response["Content-Length"] = (body.bytesize + 99).to_s expect { rest.request(:GET, url) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end context "when JSON is returned" do let(:body) { '{"ohai2u":"json_api"}' } it "should inflate the body as to an object" do http_response.add_field('content-type', "application/json") expect(rest.request(:GET, url, {})).to eq({"ohai2u"=>"json_api"}) end it "should fail if the response is truncated" do http_response.add_field('content-type', "application/json") http_response["Content-Length"] = (body.bytesize + 99).to_s expect { rest.request(:GET, url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end %w[ HTTPFound HTTPMovedPermanently HTTPSeeOther HTTPUseProxy HTTPTemporaryRedirect HTTPMultipleChoice ].each do |resp_name| describe "when encountering a #{resp_name} redirect" do let(:http_response) do resp_cls = Net.const_get(resp_name) resp_code = Net::HTTPResponse::CODE_TO_OBJ.keys.detect { |k| Net::HTTPResponse::CODE_TO_OBJ[k] == resp_cls } http_response = Net::HTTPFound.new("1.1", resp_code, "bob is somewhere else again") http_response.add_field("location", url.path) allow(http_response).to receive(:read_body) http_response end it "should call request again" do expect { rest.request(:GET, url) }.to raise_error(Chef::Exceptions::RedirectLimitExceeded) [:PUT, :POST, :DELETE].each do |method| expect { rest.request(method, url) }.to raise_error(Chef::Exceptions::InvalidRedirect) end end end end context "when the response is 304 NotModified" do let (:http_response) do http_response = Net::HTTPNotModified.new("1.1", "304", "it's the same as when you asked 5 minutes ago") allow(http_response).to receive(:read_body) http_response end it "should return `false`" do expect(rest.request(:GET, url)).to be_falsey end end describe "when the request fails" do before do @original_log_level = Chef::Log.level Chef::Log.level = :info end after do Chef::Log.level = @original_log_level end context "on an unsuccessful response with a JSON error" do let(:http_response) do http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth") http_response.add_field("content-type", "application/json") allow(http_response).to receive(:body).and_return('{ "error":[ "Ears get sore!", "Not even four" ] }') allow(http_response).to receive(:read_body) http_response end it "should show the JSON error message" do allow(rest).to receive(:sleep) expect {rest.request(:GET, url)}.to raise_error(Net::HTTPFatalError) expect(log_stringio.string).to match(Regexp.escape('INFO: HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four')) end end context "on an unsuccessful response with a JSON error that is compressed" do let(:http_response) do http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth") http_response.add_field("content-type", "application/json") http_response.add_field("content-encoding", "deflate") unzipped_body = '{ "error":[ "Ears get sore!", "Not even four" ] }' gzipped_body = Zlib::Deflate.deflate(unzipped_body) gzipped_body.force_encoding(Encoding::BINARY) if "strings".respond_to?(:force_encoding) allow(http_response).to receive(:body).and_return gzipped_body allow(http_response).to receive(:read_body) http_response end before do allow(rest).to receive(:sleep) allow(rest).to receive(:http_retry_count).and_return(0) end it "decompresses the JSON error message" do expect {rest.request(:GET, url)}.to raise_error(Net::HTTPFatalError) expect(log_stringio.string).to match(Regexp.escape('INFO: HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four')) end it "fails when the compressed body is truncated" do http_response["Content-Length"] = (body.bytesize + 99).to_s expect {rest.request(:GET, url)}.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end context "on a generic unsuccessful request" do let(:http_response) do http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth") allow(http_response).to receive(:body) allow(http_response).to receive(:read_body) http_response end it "retries then throws an exception" do allow(rest).to receive(:sleep) expect {rest.request(:GET, url)}.to raise_error(Net::HTTPFatalError) count = Chef::Config[:http_retry_count] expect(log_stringio.string).to match(Regexp.escape("ERROR: Server returned error 500 for #{url}, retrying #{count}/#{count}")) end end end end context "when streaming downloads to a tempfile" do let!(:tempfile) { Tempfile.open("chef-rspec-rest_spec-line-@{__LINE__}--") } let(:request_mock) { {} } let(:http_response) do http_response = Net::HTTPSuccess.new("1.1",'200', "it-works") allow(http_response).to receive(:read_body) expect(http_response).not_to receive(:body) http_response["Content-Length"] = "0" # call set_content_length (in test), if otherwise http_response end def set_content_length content_length = 0 http_response.read_body do |chunk| content_length += chunk.bytesize end http_response["Content-Length"] = content_length.to_s end before do allow(Tempfile).to receive(:new).with("chef-rest").and_return(tempfile) allow(Net::HTTP::Get).to receive(:new).and_return(request_mock) end after do tempfile.close! end it " build a new HTTP GET request without the application/json accept header" do expected_headers = {'Accept' => "*/*", 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, 'Host' => host_header, 'X-REMOTE-REQUEST-ID'=> request_id } expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock) rest.streaming_request(url, {}) end it "build a new HTTP GET request with the X-Remote-Request-Id header" do expected_headers = {'Accept' => "*/*", 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, 'Host' => host_header, 'X-REMOTE-REQUEST-ID'=> request_id } expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock) rest.streaming_request(url, {}) end it "returns a tempfile containing the streamed response body" do expect(rest.streaming_request(url, {})).to equal(tempfile) end it "writes the response body to a tempfile" do allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power") set_content_length rest.streaming_request(url, {}) expect(IO.read(tempfile.path).chomp).to eq("realultimatepower") end it "closes the tempfile" do rest.streaming_request(url, {}) expect(tempfile).to be_closed end it "yields the tempfile containing the streamed response body and then unlinks it when given a block" do allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power") set_content_length tempfile_path = nil rest.streaming_request(url, {}) do |tempfile| tempfile_path = tempfile.path expect(File.exist?(tempfile.path)).to be_truthy expect(IO.read(tempfile.path).chomp).to eq("realultimatepower") end expect(File.exist?(tempfile_path)).to be_falsey end it "does not raise a divide by zero exception if the content's actual size is 0" do http_response['Content-Length'] = "5" allow(http_response).to receive(:read_body).and_yield('') expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end it "does not raise a divide by zero exception when the Content-Length is 0" do http_response['Content-Length'] = "0" allow(http_response).to receive(:read_body).and_yield("ninja") expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end it "it raises an exception when the download is truncated" do http_response["Content-Length"] = (body.bytesize + 99).to_s allow(http_response).to receive(:read_body).and_yield("ninja") expect { rest.streaming_request(url, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end it "fetches a file and yields the tempfile it is streamed to" do allow(http_response).to receive(:read_body).and_yield("real").and_yield("ultimate").and_yield("power") set_content_length tempfile_path = nil rest.fetch("cookbooks/a_cookbook") do |tempfile| tempfile_path = tempfile.path expect(IO.read(tempfile.path).chomp).to eq("realultimatepower") end expect(File.exist?(tempfile_path)).to be_falsey end it "closes and unlinks the tempfile if there is an error while streaming the content to the tempfile" do path = tempfile.path expect(path).not_to be_nil allow(tempfile).to receive(:write).and_raise(IOError) rest.fetch("cookbooks/a_cookbook") {|tmpfile| "shouldn't get here"} expect(File.exists?(path)).to be_falsey end it "closes and unlinks the tempfile when the response is a redirect" do tempfile = double("A tempfile", :path => "/tmp/ragefist", :close => true, :binmode => true) expect(tempfile).to receive(:close!).at_least(1).times allow(Tempfile).to receive(:new).with("chef-rest").and_return(tempfile) redirect = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today") redirect.add_field("location", url.path) allow(redirect).to receive(:read_body) expect(http_client).to receive(:request).and_yield(redirect).and_return(redirect) expect(http_client).to receive(:request).and_yield(http_response).and_return(http_response) rest.fetch("cookbooks/a_cookbook") {|tmpfile| "shouldn't get here"} end it "passes the original block to the redirected request" do http_redirect = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today") http_redirect.add_field("location","/that-thing-is-here-now") allow(http_redirect).to receive(:read_body) block_called = false allow(http_client).to receive(:request).and_yield(http_response).and_return(http_redirect, http_response) rest.fetch("cookbooks/a_cookbook") do |tmpfile| block_called = true end expect(block_called).to be_truthy end end end context "when following redirects" do let(:rest) do Chef::REST.new(base_url) end before do Chef::Config[:node_name] = "webmonkey.example.com" Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" end it "raises a RedirectLimitExceeded when redirected more than 10 times" do redirected = lambda {rest.follow_redirect { redirected.call }} expect {redirected.call}.to raise_error(Chef::Exceptions::RedirectLimitExceeded) end it "does not count redirects from previous calls against the redirect limit" do total_redirects = 0 redirected = lambda do rest.follow_redirect do total_redirects += 1 redirected.call unless total_redirects >= 9 end end expect {redirected.call}.not_to raise_error total_redirects = 0 expect {redirected.call}.not_to raise_error end it "does not sign the redirected request when sign_on_redirect is false" do rest.sign_on_redirect = false rest.follow_redirect { expect(rest.sign_requests?).to be_falsey } end it "resets sign_requests to the original value after following an unsigned redirect" do rest.sign_on_redirect = false expect(rest.sign_requests?).to be_truthy rest.follow_redirect { expect(rest.sign_requests?).to be_falsey } expect(rest.sign_requests?).to be_truthy end it "configures the redirect limit" do total_redirects = 0 redirected = lambda do rest.follow_redirect do total_redirects += 1 redirected.call unless total_redirects >= 9 end end expect {redirected.call}.not_to raise_error total_redirects = 0 rest.redirect_limit = 3 expect {redirected.call}.to raise_error(Chef::Exceptions::RedirectLimitExceeded) end end end chef-12.3.0/spec/unit/resource_spec.rb0000644000004100000410000007534212520074675017654 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Author:: Seth Chisamore () # Copyright:: Copyright (c) 2008-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 'spec_helper' class ResourceTestHarness < Chef::Resource provider_base Chef::Provider::Package end describe Chef::Resource do before(:each) do @cookbook_repo_path = File.join(CHEF_SPEC_DATA, 'cookbooks') @cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(@cookbook_repo_path)) @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @resource = Chef::Resource.new("funk", @run_context) end describe "when inherited" do it "adds an entry to a list of subclasses" do subclass = Class.new(Chef::Resource) expect(Chef::Resource.resource_classes).to include(subclass) end it "keeps track of subclasses of subclasses" do subclass = Class.new(Chef::Resource) subclass_of_subclass = Class.new(subclass) expect(Chef::Resource.resource_classes).to include(subclass_of_subclass) end end describe "when declaring the identity attribute" do it "has no identity attribute by default" do expect(Chef::Resource.identity_attr).to be_nil end it "sets an identity attribute" do resource_class = Class.new(Chef::Resource) resource_class.identity_attr(:path) expect(resource_class.identity_attr).to eq(:path) end it "inherits an identity attribute from a superclass" do resource_class = Class.new(Chef::Resource) resource_subclass = Class.new(resource_class) resource_class.identity_attr(:package_name) expect(resource_subclass.identity_attr).to eq(:package_name) end it "overrides the identity attribute from a superclass when the identity attr is set" do resource_class = Class.new(Chef::Resource) resource_subclass = Class.new(resource_class) resource_class.identity_attr(:package_name) resource_subclass.identity_attr(:something_else) expect(resource_subclass.identity_attr).to eq(:something_else) end end describe "when no identity attribute has been declared" do before do @resource_sans_id = Chef::Resource.new("my-name") end # Would rather force identity attributes to be set for everything, # but that's not plausible for back compat reasons. it "uses the name as the identity" do expect(@resource_sans_id.identity).to eq("my-name") end end describe "when an identity attribute has been declared" do before do @file_resource_class = Class.new(Chef::Resource) do identity_attr :path attr_accessor :path end @file_resource = @file_resource_class.new("identity-attr-test") @file_resource.path = "/tmp/foo.txt" end it "gives the value of its identity attribute" do expect(@file_resource.identity).to eq("/tmp/foo.txt") end end describe "when declaring state attributes" do it "has no state_attrs by default" do expect(Chef::Resource.state_attrs).to be_empty end it "sets a list of state attributes" do resource_class = Class.new(Chef::Resource) resource_class.state_attrs(:checksum, :owner, :group, :mode) expect(resource_class.state_attrs).to match_array([:checksum, :owner, :group, :mode]) end it "inherits state attributes from the superclass" do resource_class = Class.new(Chef::Resource) resource_subclass = Class.new(resource_class) resource_class.state_attrs(:checksum, :owner, :group, :mode) expect(resource_subclass.state_attrs).to match_array([:checksum, :owner, :group, :mode]) end it "combines inherited state attributes with non-inherited state attributes" do resource_class = Class.new(Chef::Resource) resource_subclass = Class.new(resource_class) resource_class.state_attrs(:checksum, :owner) resource_subclass.state_attrs(:group, :mode) expect(resource_subclass.state_attrs).to match_array([:checksum, :owner, :group, :mode]) end end describe "when a set of state attributes has been declared" do before do @file_resource_class = Class.new(Chef::Resource) do state_attrs :checksum, :owner, :group, :mode attr_accessor :checksum attr_accessor :owner attr_accessor :group attr_accessor :mode end @file_resource = @file_resource_class.new("describe-state-test") @file_resource.checksum = "abc123" @file_resource.owner = "root" @file_resource.group = "wheel" @file_resource.mode = "0644" end it "describes its state" do resource_state = @file_resource.state expect(resource_state.keys).to match_array([:checksum, :owner, :group, :mode]) expect(resource_state[:checksum]).to eq("abc123") expect(resource_state[:owner]).to eq("root") expect(resource_state[:group]).to eq("wheel") expect(resource_state[:mode]).to eq("0644") end end describe "load_from" do before(:each) do @prior_resource = Chef::Resource.new("funk") @prior_resource.supports(:funky => true) @prior_resource.source_line @prior_resource.allowed_actions << :funkytown @prior_resource.action(:funkytown) @resource.allowed_actions << :funkytown @run_context.resource_collection << @prior_resource end it "should load the attributes of a prior resource" do @resource.load_from(@prior_resource) expect(@resource.supports).to eq({ :funky => true }) end it "should not inherit the action from the prior resource" do @resource.load_from(@prior_resource) expect(@resource.action).not_to eq(@prior_resource.action) end end describe "name" do it "should have a name" do expect(@resource.name).to eql("funk") end it "should let you set a new name" do @resource.name "monkey" expect(@resource.name).to eql("monkey") end it "coerces arrays to names" do expect(@resource.name ['a', 'b']).to eql('a, b') end it "should coerce objects to a string" do expect(@resource.name Object.new).to be_a(String) end end describe "noop" do it "should accept true or false for noop" do expect { @resource.noop true }.not_to raise_error expect { @resource.noop false }.not_to raise_error expect { @resource.noop "eat it" }.to raise_error(ArgumentError) end end describe "notifies" do it "should make notified resources appear in the actions hash" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "coffee") expect(@resource.delayed_notifications.detect{|e| e.resource.name == "coffee" && e.action == :reload}).not_to be_nil end it "should make notified resources be capable of acting immediately" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "coffee"), :immediate expect(@resource.immediate_notifications.detect{|e| e.resource.name == "coffee" && e.action == :reload}).not_to be_nil end it "should raise an exception if told to act in other than :delay or :immediate(ly)" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") expect { @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "coffee"), :someday }.to raise_error(ArgumentError) end it "should allow multiple notified resources appear in the actions hash" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "coffee") expect(@resource.delayed_notifications.detect{|e| e.resource.name == "coffee" && e.action == :reload}).not_to be_nil @run_context.resource_collection << Chef::Resource::ZenMaster.new("beans") @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "beans") expect(@resource.delayed_notifications.detect{|e| e.resource.name == "beans" && e.action == :reload}).not_to be_nil end it "creates a notification for a resource that is not yet in the resource collection" do @resource.notifies(:restart, :service => 'apache') expected_notification = Chef::Resource::Notification.new({:service => "apache"}, :restart, @resource) expect(@resource.delayed_notifications).to include(expected_notification) end it "notifies another resource immediately" do @resource.notifies_immediately(:restart, :service => 'apache') expected_notification = Chef::Resource::Notification.new({:service => "apache"}, :restart, @resource) expect(@resource.immediate_notifications).to include(expected_notification) end it "notifies a resource to take action at the end of the chef run" do @resource.notifies_delayed(:restart, :service => "apache") expected_notification = Chef::Resource::Notification.new({:service => "apache"}, :restart, @resource) expect(@resource.delayed_notifications).to include(expected_notification) end it "notifies a resource with an array for its name via its prettified string name" do @run_context.resource_collection << Chef::Resource::ZenMaster.new(["coffee", "tea"]) @resource.notifies :reload, @run_context.resource_collection.find(:zen_master => "coffee, tea") expect(@resource.delayed_notifications.detect{|e| e.resource.name == "coffee, tea" && e.action == :reload}).not_to be_nil end end describe "subscribes" do it "should make resources appear in the actions hash of subscribed nodes" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") zr = @run_context.resource_collection.find(:zen_master => "coffee") @resource.subscribes :reload, zr expect(zr.delayed_notifications.detect{|e| e.resource.name == "funk" && e.action == :reload}).not_to be_nil end it "should make resources appear in the actions hash of subscribed nodes" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") zr = @run_context.resource_collection.find(:zen_master => "coffee") @resource.subscribes :reload, zr expect(zr.delayed_notifications.detect{|e| e.resource.name == @resource.name && e.action == :reload}).not_to be_nil @run_context.resource_collection << Chef::Resource::ZenMaster.new("bean") zrb = @run_context.resource_collection.find(:zen_master => "bean") zrb.subscribes :reload, zr expect(zr.delayed_notifications.detect{|e| e.resource.name == @resource.name && e.action == :reload}).not_to be_nil end it "should make subscribed resources be capable of acting immediately" do @run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee") zr = @run_context.resource_collection.find(:zen_master => "coffee") @resource.subscribes :reload, zr, :immediately expect(zr.immediate_notifications.detect{|e| e.resource.name == @resource.name && e.action == :reload}).not_to be_nil end end describe "defined_at" do it "should correctly parse source_line on unix-like operating systems" do @resource.source_line = "/some/path/to/file.rb:80:in `wombat_tears'" expect(@resource.defined_at).to eq("/some/path/to/file.rb line 80") end it "should correctly parse source_line on Windows" do @resource.source_line = "C:/some/path/to/file.rb:80 in 1`wombat_tears'" expect(@resource.defined_at).to eq("C:/some/path/to/file.rb line 80") end it "should include the cookbook and recipe when it knows it" do @resource.source_line = "/some/path/to/file.rb:80:in `wombat_tears'" @resource.recipe_name = "wombats" @resource.cookbook_name = "animals" expect(@resource.defined_at).to eq("animals::wombats line 80") end it "should recognize dynamically defined resources" do expect(@resource.defined_at).to eq("dynamically defined") end end describe "to_s" do it "should become a string like resource_name[name]" do zm = Chef::Resource::ZenMaster.new("coffee") expect(zm.to_s).to eql("zen_master[coffee]") end end describe "is" do it "should return the arguments passed with 'is'" do zm = Chef::Resource::ZenMaster.new("coffee") expect(zm.is("one", "two", "three")).to eq(%w|one two three|) end it "should allow arguments preceded by is to methods" do @resource.noop(@resource.is(true)) expect(@resource.noop).to eql(true) end end describe "to_json" do it "should serialize to json" do json = @resource.to_json expect(json).to match(/json_class/) expect(json).to match(/instance_vars/) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @resource } end end describe "to_hash" do it "should convert to a hash" do hash = @resource.to_hash expected_keys = [ :allowed_actions, :params, :provider, :updated, :updated_by_last_action, :before, :supports, :noop, :ignore_failure, :name, :source_line, :action, :retries, :retry_delay, :elapsed_time, :default_guard_interpreter, :guard_interpreter, :sensitive ] expect(hash.keys - expected_keys).to eq([]) expect(expected_keys - hash.keys).to eq([]) expect(hash[:name]).to eql("funk") end end describe "self.json_create" do it "should deserialize itself from json" do json = Chef::JSONCompat.to_json(@resource) serialized_node = Chef::JSONCompat.from_json(json) expect(serialized_node).to be_a_kind_of(Chef::Resource) expect(serialized_node.name).to eql(@resource.name) end end describe "supports" do it "should allow you to set what features this resource supports" do support_hash = { :one => :two } @resource.supports(support_hash) expect(@resource.supports).to eql(support_hash) end it "should return the current value of supports" do expect(@resource.supports).to eq({}) end end describe "ignore_failure" do it "should default to throwing an error if a provider fails for a resource" do expect(@resource.ignore_failure).to eq(false) end it "should allow you to set whether a provider should throw exceptions with ignore_failure" do @resource.ignore_failure(true) expect(@resource.ignore_failure).to eq(true) end it "should allow you to epic_fail" do @resource.epic_fail(true) expect(@resource.epic_fail).to eq(true) end end describe "retries" do before do @retriable_resource = Chef::Resource::Cat.new("precious", @run_context) @retriable_resource.provider = Chef::Provider::SnakeOil @retriable_resource.action = :purr @node.automatic_attrs[:platform] = "fubuntu" @node.automatic_attrs[:platform_version] = '10.04' end it "should default to not retrying if a provider fails for a resource" do expect(@retriable_resource.retries).to eq(0) end it "should allow you to set how many retries a provider should attempt after a failure" do @retriable_resource.retries(2) expect(@retriable_resource.retries).to eq(2) end it "should default to a retry delay of 2 seconds" do expect(@retriable_resource.retry_delay).to eq(2) end it "should allow you to set the retry delay" do @retriable_resource.retry_delay(10) expect(@retriable_resource.retry_delay).to eq(10) end it "should keep given value of retries intact after the provider fails for a resource" do @retriable_resource.retries(3) @retriable_resource.retry_delay(0) # No need to wait. provider = Chef::Provider::SnakeOil.new(@retriable_resource, @run_context) allow(Chef::Provider::SnakeOil).to receive(:new).and_return(provider) allow(provider).to receive(:action_purr).and_raise expect(@retriable_resource).to receive(:sleep).exactly(3).times expect { @retriable_resource.run_action(:purr) }.to raise_error expect(@retriable_resource.retries).to eq(3) end end describe "setting the base provider class for the resource" do it "defaults to Chef::Provider for the base class" do expect(Chef::Resource.provider_base).to eq(Chef::Provider) end it "allows the base provider to be overriden by a " do expect(ResourceTestHarness.provider_base).to eq(Chef::Provider::Package) end end it "runs an action by finding its provider, loading the current resource and then running the action" do skip end describe "when updated by a provider" do before do @resource.updated_by_last_action(true) end it "records that it was updated" do expect(@resource).to be_updated end it "records that the last action updated the resource" do expect(@resource).to be_updated_by_last_action end describe "and then run again without being updated" do before do @resource.updated_by_last_action(false) end it "reports that it is updated" do expect(@resource).to be_updated end it "reports that it was not updated by the last action" do expect(@resource).not_to be_updated_by_last_action end end end describe "when invoking its action" do before do @resource = Chef::Resource.new("provided", @run_context) @resource.provider = Chef::Provider::SnakeOil @node.automatic_attrs[:platform] = "fubuntu" @node.automatic_attrs[:platform_version] = '10.04' end it "does not run only_if if no only_if command is given" do expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:evaluate) @resource.only_if.clear @resource.run_action(:purr) end it "runs runs an only_if when one is given" do snitch_variable = nil @resource.only_if { snitch_variable = true } expect(@resource.only_if.first.positivity).to eq(:only_if) #Chef::Mixin::Command.should_receive(:only_if).with(true, {}).and_return(false) @resource.run_action(:purr) expect(snitch_variable).to be_truthy end it "runs multiple only_if conditionals" do snitch_var1, snitch_var2 = nil, nil @resource.only_if { snitch_var1 = 1 } @resource.only_if { snitch_var2 = 2 } @resource.run_action(:purr) expect(snitch_var1).to eq(1) expect(snitch_var2).to eq(2) end it "accepts command options for only_if conditionals" do expect_any_instance_of(Chef::Resource::Conditional).to receive(:evaluate_command).at_least(1).times @resource.only_if("true", :cwd => '/tmp') expect(@resource.only_if.first.command_opts).to eq({:cwd => '/tmp'}) @resource.run_action(:purr) end it "runs not_if as a command when it is a string" do expect_any_instance_of(Chef::Resource::Conditional).to receive(:evaluate_command).at_least(1).times @resource.not_if "pwd" @resource.run_action(:purr) end it "runs not_if as a block when it is a ruby block" do expect_any_instance_of(Chef::Resource::Conditional).to receive(:evaluate_block).at_least(1).times @resource.not_if { puts 'foo' } @resource.run_action(:purr) end it "does not run not_if if no not_if command is given" do expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:evaluate) @resource.not_if.clear @resource.run_action(:purr) end it "accepts command options for not_if conditionals" do @resource.not_if("pwd" , :cwd => '/tmp') expect(@resource.not_if.first.command_opts).to eq({:cwd => '/tmp'}) end it "accepts multiple not_if conditionals" do snitch_var1, snitch_var2 = true, true @resource.not_if {snitch_var1 = nil} @resource.not_if {snitch_var2 = false} @resource.run_action(:purr) expect(snitch_var1).to be_nil expect(snitch_var2).to be_falsey end it "reports 0 elapsed time if actual elapsed time is < 0" do expected = Time.now allow(Time).to receive(:now).and_return(expected, expected - 1) @resource.run_action(:purr) expect(@resource.elapsed_time).to eq(0) end describe "guard_interpreter attribute" do let(:resource) { @resource } it "should be set to :default by default" do expect(resource.guard_interpreter).to eq(:default) end it "if set to :default should return :default when read" do resource.guard_interpreter(:default) expect(resource.guard_interpreter).to eq(:default) end it "should raise Chef::Exceptions::ValidationFailed on an attempt to set the guard_interpreter attribute to something other than a Symbol" do expect { resource.guard_interpreter('command_dot_com') }.to raise_error(Chef::Exceptions::ValidationFailed) end it "should not raise an exception when setting the guard interpreter attribute to a Symbol" do allow(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:new).and_return(nil) expect { resource.guard_interpreter(:command_dot_com) }.not_to raise_error end end end describe "should_skip?" do before do @resource = Chef::Resource::Cat.new("sugar", @run_context) end it "should return false by default" do expect(@resource.should_skip?(:purr)).to be_falsey end it "should return false when only_if is met" do @resource.only_if { true } expect(@resource.should_skip?(:purr)).to be_falsey end it "should return true when only_if is not met" do @resource.only_if { false } expect(@resource.should_skip?(:purr)).to be_truthy end it "should return true when not_if is met" do @resource.not_if { true } expect(@resource.should_skip?(:purr)).to be_truthy end it "should return false when not_if is not met" do @resource.not_if { false } expect(@resource.should_skip?(:purr)).to be_falsey end it "should return true when only_if is met but also not_if is met" do @resource.only_if { true } @resource.not_if { true } expect(@resource.should_skip?(:purr)).to be_truthy end it "should return true when one of multiple only_if's is not met" do @resource.only_if { true } @resource.only_if { false } @resource.only_if { true } expect(@resource.should_skip?(:purr)).to be_truthy end it "should return true when one of multiple not_if's is met" do @resource.not_if { false } @resource.not_if { true } @resource.not_if { false } expect(@resource.should_skip?(:purr)).to be_truthy end it "should return true when action is :nothing" do expect(@resource.should_skip?(:nothing)).to be_truthy end it "should return true when action is :nothing ignoring only_if/not_if conditionals" do @resource.only_if { true } @resource.not_if { false } expect(@resource.should_skip?(:nothing)).to be_truthy end it "should print \"skipped due to action :nothing\" message for doc formatter when action is :nothing" do fdoc = Chef::Formatters.new(:doc, STDOUT, STDERR) allow(@run_context).to receive(:events).and_return(fdoc) expect(fdoc).to receive(:puts).with(" (skipped due to action :nothing)", anything()) @resource.should_skip?(:nothing) end end describe "when resource action is :nothing" do before do @resource1 = Chef::Resource::Cat.new("sugar", @run_context) @resource1.action = :nothing @node.automatic_attrs[:platform] = "fubuntu" @node.automatic_attrs[:platform_version] = '10.04' end it "should not run only_if/not_if conditionals (CHEF-972)" do snitch_var1 = 0 @resource1.only_if { snitch_var1 = 1 } @resource1.not_if { snitch_var1 = 2 } @resource1.run_action(:nothing) expect(snitch_var1).to eq(0) end it "should run only_if/not_if conditionals when notified to run another action (CHEF-972)" do snitch_var1 = snitch_var2 = 0 @runner = Chef::Runner.new(@run_context) Chef::Platform.set( :resource => :cat, :provider => Chef::Provider::SnakeOil ) @resource1.only_if { snitch_var1 = 1 } @resource1.not_if { snitch_var2 = 2 } @resource2 = Chef::Resource::Cat.new("coffee", @run_context) @resource2.notifies :purr, @resource1 @resource2.action = :purr @run_context.resource_collection << @resource1 @run_context.resource_collection << @resource2 @runner.converge expect(snitch_var1).to eq(1) expect(snitch_var2).to eq(2) end end describe "building the platform map" do let(:klz) { Class.new(Chef::Resource) } before do Chef::Resource::Klz = klz end after do Chef::Resource.send(:remove_const, :Klz) end it 'adds mappings for a single platform' do expect(Chef::Resource::Klz.node_map).to receive(:set).with( :dinobot, true, { platform: ['autobots'] } ) klz.provides :dinobot, platform: ['autobots'] end it 'adds mappings for multiple platforms' do expect(Chef::Resource::Klz.node_map).to receive(:set).with( :energy, true, { platform: ['autobots', 'decepticons']} ) klz.provides :energy, platform: ['autobots', 'decepticons'] end it 'adds mappings for all platforms' do expect(Chef::Resource::Klz.node_map).to receive(:set).with( :tape_deck, true, {} ) klz.provides :tape_deck end end describe "lookups from the platform map" do let(:klz1) { Class.new(Chef::Resource) } let(:klz2) { Class.new(Chef::Resource) } before(:each) do Chef::Resource::Klz1 = klz1 Chef::Resource::Klz2 = klz2 @node = Chef::Node.new @node.name("bumblebee") @node.automatic[:platform] = "autobots" @node.automatic[:platform_version] = "6.1" Object.const_set('Soundwave', klz1) klz2.provides :dinobot, :on_platforms => ['autobots'] Object.const_set('Grimlock', klz2) end after(:each) do Object.send(:remove_const, :Soundwave) Object.send(:remove_const, :Grimlock) Chef::Resource.send(:remove_const, :Klz1) Chef::Resource.send(:remove_const, :Klz2) end describe "resource_for_node" do it "returns a resource by short_name and node" do expect(Chef::Resource.resource_for_node(:dinobot, @node)).to eql(Grimlock) end it "returns a resource by short_name if nothing else matches" do expect(Chef::Resource.resource_for_node(:soundwave, @node)).to eql(Soundwave) end end end describe "when creating notifications" do describe "with a string resource spec" do it "creates a delayed notification when timing is not specified" do @resource.notifies(:run, "execute[foo]") expect(@run_context.delayed_notification_collection.size).to eq(1) end it "creates a delayed notification when :delayed is not specified" do @resource.notifies(:run, "execute[foo]", :delayed) expect(@run_context.delayed_notification_collection.size).to eq(1) end it "creates an immediate notification when :immediate is specified" do @resource.notifies(:run, "execute[foo]", :immediate) expect(@run_context.immediate_notification_collection.size).to eq(1) end it "creates an immediate notification when :immediately is specified" do @resource.notifies(:run, "execute[foo]", :immediately) expect(@run_context.immediate_notification_collection.size).to eq(1) end describe "with a syntax error in the resource spec" do it "raises an exception immmediately" do expect do @resource.notifies(:run, "typo[missing-closing-bracket") end.to raise_error(Chef::Exceptions::InvalidResourceSpecification) end end end describe "with a resource reference" do before do @notified_resource = Chef::Resource.new("punk", @run_context) end it "creates a delayed notification when timing is not specified" do @resource.notifies(:run, @notified_resource) expect(@run_context.delayed_notification_collection.size).to eq(1) end it "creates a delayed notification when :delayed is not specified" do @resource.notifies(:run, @notified_resource, :delayed) expect(@run_context.delayed_notification_collection.size).to eq(1) end it "creates an immediate notification when :immediate is specified" do @resource.notifies(:run, @notified_resource, :immediate) expect(@run_context.immediate_notification_collection.size).to eq(1) end it "creates an immediate notification when :immediately is specified" do @resource.notifies(:run, @notified_resource, :immediately) expect(@run_context.immediate_notification_collection.size).to eq(1) end end end describe "resource sensitive attribute" do before(:each) do @resource_file = Chef::Resource::File.new("/nonexistent/CHEF-5098/file", @run_context) @action = :create end def compiled_resource_data(resource, action, err) error_inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(resource, action, err) description = Chef::Formatters::ErrorDescription.new("test") error_inspector.add_explanation(description) Chef::Log.info("descrtiption: #{description.inspect},error_inspector: #{error_inspector}") description.sections[1]["Compiled Resource:"] end it "set to false by default" do expect(@resource.sensitive).to be_falsey end it "when set to false should show compiled resource for failed resource" do expect { @resource_file.run_action(@action) }.to raise_error { |err| expect(compiled_resource_data(@resource_file, @action, err)).to match 'path "/nonexistent/CHEF-5098/file"' } end it "when set to true should show compiled resource for failed resource" do @resource_file.sensitive true expect { @resource_file.run_action(@action) }.to raise_error { |err| expect(compiled_resource_data(@resource_file, @action, err)).to eql("suppressed sensitive resource output") } end end end chef-12.3.0/spec/unit/encrypted_data_bag_item_spec.rb0000644000004100000410000003575712520074675022650 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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 'spec_helper' require 'chef/encrypted_data_bag_item' module Version0Encryptor def self.encrypt_value(plaintext_data, key) data = plaintext_data.to_yaml cipher = OpenSSL::Cipher.new("aes-256-cbc") cipher.encrypt cipher.pkcs5_keyivgen(key) encrypted_bytes = cipher.update(data) encrypted_bytes << cipher.final Base64.encode64(encrypted_bytes) end end describe Chef::EncryptedDataBagItem::Encryptor do subject(:encryptor) { described_class.new(plaintext_data, key) } let(:plaintext_data) { {"foo" => "bar"} } let(:key) { "passwd" } it "encrypts to format version 1 by default" do expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor) end describe "generating a random IV" do it "generates a new IV for each encryption pass" do encryptor2 = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, key) # No API in ruby OpenSSL to get the iv is used for the encryption back # out. Instead we test if the encrypted data is the same. If it *is* the # same, we assume the IV was the same each time. expect(encryptor.encrypted_data).not_to eq encryptor2.encrypted_data end end describe "when encrypting a non-hash non-array value" do let(:plaintext_data) { 5 } it "serializes the value in a de-serializable way" do expect(Chef::JSONCompat.from_json(encryptor.serialized_data)["json_wrapper"]).to eq 5 end end describe "wrapping secret values in an envelope" do it "wraps the encrypted data in an envelope with the iv and version" do final_data = encryptor.for_encrypted_item expect(final_data["encrypted_data"]).to eq encryptor.encrypted_data expect(final_data["iv"]).to eq Base64.encode64(encryptor.iv) expect(final_data["version"]).to eq 1 expect(final_data["cipher"]).to eq"aes-256-cbc" end end describe "when using version 2 format" do before do Chef::Config[:data_bag_encrypt_version] = 2 end it "creates a version 2 encryptor" do expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version2Encryptor) end it "generates an hmac based on ciphertext with different iv" do encryptor2 = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, key) expect(encryptor.hmac).not_to eq(encryptor2.hmac) end it "includes the hmac in the envelope" do final_data = encryptor.for_encrypted_item expect(final_data["hmac"]).to eq(encryptor.hmac) end end describe "when using version 3 format" do before do Chef::Config[:data_bag_encrypt_version] = 3 end context "on supported platforms", :aes_256_gcm_only, :ruby_20_only do it "creates a version 3 encryptor" do expect(encryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor) end it "generates different authentication tags" do encryptor3 = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, key) encryptor.for_encrypted_item # required to generate the auth_tag encryptor3.for_encrypted_item expect(encryptor.auth_tag).not_to eq(encryptor3.auth_tag) end it "includes the auth_tag in the envelope" do final_data = encryptor.for_encrypted_item expect(final_data["auth_tag"]).to eq(Base64::encode64(encryptor.auth_tag)) end it "throws an error if auth tag is read before encrypting the data" do expect { encryptor.auth_tag }.to raise_error(Chef::EncryptedDataBagItem::EncryptionFailure) end end # context on supported platforms context "on unsupported platforms" do let(:aead_algorithm) { Chef::EncryptedDataBagItem::AEAD_ALGORITHM } it "throws an error warning about the OpenSSL version if it has no GCM support" do # Force Ruby with AEAD support allow(OpenSSL::Cipher).to receive(:method_defined?).with(:auth_data=).and_return(true) # OpenSSL without AEAD support expect(OpenSSL::Cipher).to receive(:ciphers).and_return([]) expect { encryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/) end context "on platforms with old OpenSSL", :openssl_lt_101 do it "throws an error warning about the OpenSSL version" do expect { encryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/) end end # context on platforms with old OpenSSL end # context on unsupported platforms end # when using version 3 format end describe Chef::EncryptedDataBagItem::Decryptor do subject(:decryptor) { described_class.for(encrypted_value, decryption_key) } let(:plaintext_data) { {"foo" => "bar"} } let(:encryption_key) { "passwd" } let(:decryption_key) { encryption_key } let(:json_wrapped_data) { Chef::JSONCompat.to_json({"json_wrapper" => plaintext_data}) } shared_examples "decryption examples" do it "decrypts the encrypted value" do expect(decryptor.decrypted_data).to eq(json_wrapped_data) end it "unwraps the encrypted data and returns it" do expect(decryptor.for_decrypted_item).to eq plaintext_data end end context "when decrypting a version 3 (JSON+aes-256-gcm+random iv+auth tag) encrypted value" do context "on supported platforms", :aes_256_gcm_only, :ruby_20_only do let(:encrypted_value) do Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.new(plaintext_data, encryption_key).for_encrypted_item end let(:bogus_auth_tag) { "bogus_auth_tag" } include_examples "decryption examples" it "rejects the data if the authentication tag is wrong" do encrypted_value["auth_tag"] = bogus_auth_tag expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end it "rejects the data if the authentication tag is missing" do encrypted_value.delete("auth_tag") expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end end # context on supported platforms context "on unsupported platforms" do let(:encrypted_value) do { "encrypted_data" => "", "iv" => "", "version" => 3, "cipher" => "aes-256-cbc", } end context "on platforms with old OpenSSL", :openssl_lt_101 do it "throws an error warning about the OpenSSL version" do expect { decryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/) end end # context on unsupported platforms end # context on platforms with old OpenSSL end # context when decrypting a version 3 context "when decrypting a version 2 (JSON+aes-256-cbc+hmac-sha256+random iv) encrypted value" do let(:encrypted_value) do Chef::EncryptedDataBagItem::Encryptor::Version2Encryptor.new(plaintext_data, encryption_key).for_encrypted_item end let(:bogus_hmac) do digest = OpenSSL::Digest.new("sha256") raw_hmac = OpenSSL::HMAC.digest(digest, "WRONG", encrypted_value["encrypted_data"]) Base64.encode64(raw_hmac) end include_examples "decryption examples" it "rejects the data if the hmac is wrong" do encrypted_value["hmac"] = bogus_hmac expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end it "rejects the data if the hmac is missing" do encrypted_value.delete("hmac") expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end end context "when decrypting a version 1 (JSON+aes-256-cbc+random iv) encrypted value" do let(:encrypted_value) do Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, encryption_key).for_encrypted_item end it "selects the correct strategy for version 1" do expect(decryptor).to be_a_instance_of Chef::EncryptedDataBagItem::Decryptor::Version1Decryptor end include_examples "decryption examples" describe "and the decryption step returns invalid data" do it "raises a decryption failure error" do # Over a large number of tests on a variety of systems, we occasionally # see the decryption step "succeed" but return invalid data (e.g., not # the original plain text) [CHEF-3858] expect(decryptor).to receive(:decrypted_data).and_return("lksajdf") expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end end context "and the provided key is incorrect" do let(:decryption_key) { "wrong-passwd" } it "raises a sensible error" do expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::DecryptionFailure) end end context "and the cipher is not supported" do let(:encrypted_value) do ev = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data, encryption_key).for_encrypted_item ev["cipher"] = "aes-256-foo" ev end it "raises a sensible error" do expect { decryptor.for_decrypted_item }.to raise_error(Chef::EncryptedDataBagItem::UnsupportedCipher) end end context "and version 2 format is required" do before do Chef::Config[:data_bag_decrypt_minimum_version] = 2 end it "raises an error attempting to decrypt" do expect { decryptor }.to raise_error(Chef::EncryptedDataBagItem::UnacceptableEncryptedDataBagItemFormat) end end end context "when decrypting a version 0 (YAML+aes-256-cbc+no iv) encrypted value" do let(:encrypted_value) do Version0Encryptor.encrypt_value(plaintext_data, encryption_key) end it "selects the correct strategy for version 0" do expect(decryptor).to be_a_instance_of(Chef::EncryptedDataBagItem::Decryptor::Version0Decryptor) end it "decrypts the encrypted value" do expect(decryptor.for_decrypted_item).to eq plaintext_data end context "and version 1 format is required" do before do Chef::Config[:data_bag_decrypt_minimum_version] = 1 end it "raises an error attempting to decrypt" do expect { decryptor }.to raise_error(Chef::EncryptedDataBagItem::UnacceptableEncryptedDataBagItemFormat) end end end end describe Chef::EncryptedDataBagItem do subject { described_class } let(:encrypted_data_bag_item) { subject.new(encoded_data, secret) } let(:plaintext_data) {{ "id" => "item_name", "greeting" => "hello", "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true }} }} let(:secret) { "abc123SECRET" } let(:encoded_data) { subject.encrypt_data_bag_item(plaintext_data, secret) } describe "encrypting" do it "doesn't encrypt the 'id' key" do expect(encoded_data["id"]).to eq "item_name" end it "encrypts non-collection objects" do expect(encoded_data["greeting"]["version"]).to eq 1 expect(encoded_data["greeting"]).to have_key("iv") iv = encoded_data["greeting"]["iv"] encryptor = Chef::EncryptedDataBagItem::Encryptor.new("hello", secret, iv) expect(encoded_data["greeting"]["encrypted_data"]).to eq(encryptor.for_encrypted_item["encrypted_data"]) end it "encrypts nested values" do expect(encoded_data["nested"]["version"]).to eq 1 expect(encoded_data["nested"]).to have_key("iv") iv = encoded_data["nested"]["iv"] encryptor = Chef::EncryptedDataBagItem::Encryptor.new(plaintext_data["nested"], secret, iv) expect(encoded_data["nested"]["encrypted_data"]).to eq(encryptor.for_encrypted_item["encrypted_data"]) end end describe "decrypting" do it "doesn't try to decrypt 'id'" do expect(encrypted_data_bag_item["id"]).to eq(plaintext_data["id"]) end it "decrypts 'greeting'" do expect(encrypted_data_bag_item["greeting"]).to eq(plaintext_data["greeting"]) end it "decrypts 'nested'" do expect(encrypted_data_bag_item["nested"]).to eq(plaintext_data["nested"]) end it "decrypts everyting via to_hash" do expect(encrypted_data_bag_item.to_hash).to eq(plaintext_data) end it "handles missing keys gracefully" do expect(encrypted_data_bag_item["no-such-key"]).to be_nil end end describe "loading" do it "should defer to Chef::DataBagItem.load" do allow(Chef::DataBagItem).to receive(:load).with(:the_bag, "my_codes").and_return(encoded_data) edbi = Chef::EncryptedDataBagItem.load(:the_bag, "my_codes", secret) expect(edbi["greeting"]).to eq(plaintext_data["greeting"]) end end describe ".load_secret" do let(:secret) { "opensesame" } context "when /var/mysecret exists" do before do allow(::File).to receive(:exist?).with("/var/mysecret").and_return(true) allow(IO).to receive(:read).with("/var/mysecret").and_return(secret) end it "load_secret('/var/mysecret') reads the secret" do expect(Chef::EncryptedDataBagItem.load_secret("/var/mysecret")).to eq secret end end context "when /etc/chef/encrypted_data_bag_secret exists" do before do path = Chef::Config.platform_specific_path("/etc/chef/encrypted_data_bag_secret") allow(::File).to receive(:exist?).with(path).and_return(true) allow(IO).to receive(:read).with(path).and_return(secret) end it "load_secret(nil) reads the secret" do expect(Chef::EncryptedDataBagItem.load_secret(nil)).to eq secret end end context "when /etc/chef/encrypted_data_bag_secret does not exist" do before do path = Chef::Config.platform_specific_path("/etc/chef/encrypted_data_bag_secret") allow(::File).to receive(:exist?).with(path).and_return(false) end it "load_secret(nil) emits a reasonable error message" do expect { Chef::EncryptedDataBagItem.load_secret(nil) }.to raise_error(ArgumentError, /No secret specified and no secret found at #{Chef::Config[:encrypted_data_bag_secret]}/) end end context "path argument is a URL" do before do allow(Kernel).to receive(:open).with("http://www.opscode.com/").and_return(StringIO.new(secret)) end it "reads from the URL" do expect(Chef::EncryptedDataBagItem.load_secret("http://www.opscode.com/")).to eq secret end end end end chef-12.3.0/spec/unit/run_context/0000755000004100000410000000000012520074675017023 5ustar www-datawww-datachef-12.3.0/spec/unit/run_context/cookbook_compiler_spec.rb0000644000004100000410000001640712520074675024072 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'support/lib/library_load_order' # These tests rely on fixture data in spec/data/run_context/cookbooks. # # Dependencies (circular or not) are specified by `depends` directives in the # metadata of these cookbooks. # # Side effects used to verify the behavior are implemented as code in the various file types. # describe Chef::RunContext::CookbookCompiler do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:cookbook_loader) do cl = Chef::CookbookLoader.new(chef_repo_path) cl.load_cookbooks cl end let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) } let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) } let(:cookbook_collection) { Chef::CookbookCollection.new(cookbook_loader) } # Lazy evaluation of `expansion` here is used to mutate the run list before expanding it let(:run_list_expansion) { node.run_list.expand('_default') } let(:compiler) do Chef::RunContext::CookbookCompiler.new(run_context, run_list_expansion, events) end describe "loading attribute files" do # Attribute files in the fixture data will append their # "cookbook_name::attribute_file_name" to the node's `:attr_load_order` # attribute when loaded. it "loads default.rb first, then other files in sort order" do node.run_list("dependency1::default") compiler.compile_attributes expect(node[:attr_load_order]).to eq(["dependency1::default", "dependency1::aa_first", "dependency1::zz_last"]) end it "loads dependencies before loading the depending cookbook's attributes" do # Also make sure that attributes aren't loaded twice if we have two # recipes from the same cookbook in the run list node.run_list("test-with-deps::default", "test-with-deps::server") compiler.compile_attributes # dependencies are stored in a hash so therefore unordered, but they should be loaded in sort order expect(node[:attr_load_order]).to eq(["dependency1::default", "dependency1::aa_first", "dependency1::zz_last", "dependency2::default", "test-with-deps::default"]) end it "does not follow infinite dependency loops" do node.run_list("test-with-circular-deps::default") # Circular deps should not cause infinite loops compiler.compile_attributes expect(node[:attr_load_order]).to eq(["circular-dep2::default", "circular-dep1::default", "test-with-circular-deps::default"]) end it "loads attributes from cookbooks that don't have a default.rb attribute file" do node.run_list("no-default-attr::default.rb") compiler.compile_attributes expect(node[:attr_load_order]).to eq(["no-default-attr::server"]) end end describe "loading libraries" do before do LibraryLoadOrder.reset! end # One big test for everything. Individual behaviors are tested by the attribute code above. it "loads libraries in run list order" do node.run_list("test-with-deps::default", "test-with-circular-deps::default") compiler.compile_libraries expect(LibraryLoadOrder.load_order).to eq(["dependency1", "dependency2", "test-with-deps", "circular-dep2", "circular-dep1", "test-with-circular-deps"]) end end describe "loading LWRPs" do before do LibraryLoadOrder.reset! end # One big test for everything. Individual behaviors are tested by the attribute code above. it "loads LWRPs in run list order" do node.run_list("test-with-deps::default", "test-with-circular-deps::default") compiler.compile_lwrps expect(LibraryLoadOrder.load_order).to eq(["dependency1-provider", "dependency1-resource", "dependency2-provider", "dependency2-resource", "test-with-deps-provider", "test-with-deps-resource", "circular-dep2-provider", "circular-dep2-resource", "circular-dep1-provider", "circular-dep1-resource", "test-with-circular-deps-provider", "test-with-circular-deps-resource"]) end end describe "loading resource definitions" do before do LibraryLoadOrder.reset! end # One big test for all load order concerns. Individual behaviors are tested # by the attribute code above. it "loads resource definitions in run list order" do node.run_list("test-with-deps::default", "test-with-circular-deps::default") compiler.compile_resource_definitions expect(LibraryLoadOrder.load_order).to eq(["dependency1-definition", "dependency2-definition", "test-with-deps-definition", "circular-dep2-definition", "circular-dep1-definition", "test-with-circular-deps-definition"]) end end describe "loading recipes" do # Tests for this behavior are in RunContext's tests end describe "listing cookbook order" do it "should return an array of cookbook names as symbols without duplicates" do node.run_list("test-with-circular-deps::default", "circular-dep1::default", "circular-dep2::default") expect(compiler.cookbook_order).to eq([:"circular-dep2", :"circular-dep1", :"test-with-circular-deps"]) end it "determines if a cookbook is in the list of cookbooks reachable by dependency" do node.run_list("test-with-deps::default", "test-with-deps::server") expect(compiler.cookbook_order).to eq([:dependency1, :dependency2, :"test-with-deps"]) expect(compiler.unreachable_cookbook?(:dependency1)).to be_falsey expect(compiler.unreachable_cookbook?(:dependency2)).to be_falsey expect(compiler.unreachable_cookbook?(:'test-with-deps')).to be_falsey expect(compiler.unreachable_cookbook?(:'circular-dep1')).to be_truthy expect(compiler.unreachable_cookbook?(:'circular-dep2')).to be_truthy end end end chef-12.3.0/spec/unit/resource/0000755000004100000410000000000012520074675016302 5ustar www-datawww-datachef-12.3.0/spec/unit/resource/route_spec.rb0000644000004100000410000000611012520074675020775 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2008 Bryan McLellan # 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::Route do before(:each) do @resource = Chef::Resource::Route.new("10.0.0.10") end it "should create a new Chef::Resource::Route" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Route) end it "should have a name" do expect(@resource.name).to eql("10.0.0.10") end it "should have a default action of 'add'" do expect(@resource.action).to eql([:add]) end it "should accept add or delete for action" do expect { @resource.action :add }.not_to raise_error expect { @resource.action :delete }.not_to raise_error expect { @resource.action :lolcat }.to raise_error(ArgumentError) end it "should use the object name as the target by default" do expect(@resource.target).to eql("10.0.0.10") end it "should allow you to specify the netmask" do @resource.netmask "255.255.255.0" expect(@resource.netmask).to eql("255.255.255.0") end it "should allow you to specify the gateway" do @resource.gateway "10.0.0.1" expect(@resource.gateway).to eql("10.0.0.1") end it "should allow you to specify the metric" do @resource.metric 10 expect(@resource.metric).to eql(10) end it "should allow you to specify the device" do @resource.device "eth0" expect(@resource.device).to eql("eth0") end it "should allow you to specify the route type" do @resource.route_type "host" expect(@resource.route_type).to eql(:host) end it "should default to a host route type" do expect(@resource.route_type).to eql(:host) end it "should accept a net route type" do @resource.route_type :net expect(@resource.route_type).to eql(:net) end it "should reject any other route_type but :host and :net" do expect { @resource.route_type "lolcat" }.to raise_error(ArgumentError) end describe "when it has netmask, gateway, and device" do before do @resource.target("charmander") @resource.netmask("lemask") @resource.gateway("111.111.111") @resource.device("forcefield") end it "describes its state" do state = @resource.state expect(state[:netmask]).to eq("lemask") expect(state[:gateway]).to eq("111.111.111") end it "returns the target as its identity" do expect(@resource.identity).to eq("charmander") end end end chef-12.3.0/spec/unit/resource/git_spec.rb0000644000004100000410000000261712520074675020432 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Git do static_provider_resolution( resource: Chef::Resource::Git, provider: Chef::Provider::Git, name: :git, action: :sync, ) before(:each) do @git = Chef::Resource::Git.new("my awesome webapp") end it "is a kind of Scm Resource" do expect(@git).to be_a_kind_of(Chef::Resource::Scm) expect(@git).to be_an_instance_of(Chef::Resource::Git) end it "uses aliases revision as branch" do @git.branch "HEAD" expect(@git.revision).to eql("HEAD") end it "aliases revision as reference" do @git.reference "v1.0 tag" expect(@git.revision).to eql("v1.0 tag") end end chef-12.3.0/spec/unit/resource/batch_spec.rb0000644000004100000410000000260512520074675020725 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 expect(@resource).to 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 chef-12.3.0/spec/unit/resource/dsc_script_spec.rb0000644000004100000410000001326212520074675022002 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::DscScript do let(:dsc_test_resource_name) { 'DSCTest' } context 'when Powershell supports Dsc' do let(:dsc_test_run_context) { node = Chef::Node.new node.automatic[:languages][:powershell][:version] = '4.0' empty_events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, empty_events) } let(:dsc_test_resource) { Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context) } let(:configuration_code) {'echo "This is supposed to create a configuration document."'} let(:configuration_path) {'c:/myconfigs/formatc.ps1'} let(:configuration_name) { 'formatme' } let(:configuration_data) { '@{AllNodes = @( @{ NodeName = "localhost"; PSDscAllowPlainTextPassword = $true })}' } let(:configuration_data_script) { 'c:/myconfigs/data/safedata.psd1' } it "has a default action of `:run`" do expect(dsc_test_resource.action).to eq(:run) end it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do expect(dsc_test_resource.allowed_actions.to_set).to eq([:run,:nothing].to_set) end it "allows the code attribute to be set" do dsc_test_resource.code(configuration_code) expect(dsc_test_resource.code).to eq(configuration_code) end it "allows the command attribute to be set" do dsc_test_resource.command(configuration_path) expect(dsc_test_resource.command).to eq(configuration_path) end it "allows the configuration_name attribute to be set" do dsc_test_resource.configuration_name(configuration_name) expect(dsc_test_resource.configuration_name).to eq(configuration_name) end it "allows the configuration_data attribute to be set" do dsc_test_resource.configuration_data(configuration_data) expect(dsc_test_resource.configuration_data).to eq(configuration_data) end it "allows the configuration_data_script attribute to be set" do dsc_test_resource.configuration_data_script(configuration_data_script) expect(dsc_test_resource.configuration_data_script).to eq(configuration_data_script) end context "when calling imports" do let(:module_name) { 'FooModule' } let(:module_name_b) { 'BarModule' } let(:dsc_resources) { ['ResourceA', 'ResourceB'] } it "allows an arbitrary number of resources to be set for a module to be set" do dsc_test_resource.imports module_name, *dsc_resources module_imports = dsc_test_resource.imports[module_name] expect(module_imports).to eq(dsc_resources) end it "adds * to the imports when no resources are set for a moudle" do dsc_test_resource.imports module_name module_imports = dsc_test_resource.imports[module_name] expect(module_imports).to eq(['*']) end it "allows an arbitrary number of modules" do dsc_test_resource.imports module_name dsc_test_resource.imports module_name_b expect(dsc_test_resource.imports).to have_key(module_name) expect(dsc_test_resource.imports).to have_key(module_name_b) end it "allows resources to be added for a module" do dsc_test_resource.imports module_name, dsc_resources[0] dsc_test_resource.imports module_name, dsc_resources[1] module_imports = dsc_test_resource.imports[module_name] expect(module_imports).to eq(dsc_resources) end end it "raises an ArgumentError exception if an attempt is made to set the code attribute when the command attribute is already set" do dsc_test_resource.command(configuration_path) expect { dsc_test_resource.code(configuration_code) }.to raise_error(ArgumentError) end it "raises an ArgumentError exception if an attempt is made to set the command attribute when the code attribute is already set" do dsc_test_resource.code(configuration_code) expect { dsc_test_resource.command(configuration_path) }.to raise_error(ArgumentError) end it "raises an ArgumentError exception if an attempt is made to set the configuration_name attribute when the code attribute is already set" do dsc_test_resource.code(configuration_code) expect { dsc_test_resource.configuration_name(configuration_name) }.to raise_error(ArgumentError) end it "raises an ArgumentError exception if an attempt is made to set the configuration_data attribute when the configuration_data_script attribute is already set" do dsc_test_resource.configuration_data_script(configuration_data_script) expect { dsc_test_resource.configuration_data(configuration_data) }.to raise_error(ArgumentError) end it "raises an ArgumentError exception if an attempt is made to set the configuration_data_script attribute when the configuration_data attribute is already set" do dsc_test_resource.configuration_data(configuration_data) expect { dsc_test_resource.configuration_data_script(configuration_data_script) }.to raise_error(ArgumentError) end end end chef-12.3.0/spec/unit/resource/pacman_package_spec.rb0000644000004100000410000000177612520074675022566 0ustar www-datawww-data# # Author:: Jan Zimmek () # Copyright:: Copyright (c) 2010 Jan Zimmek # 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/resource/static_provider_resolution' describe Chef::Resource::PacmanPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::PacmanPackage, provider: Chef::Provider::Package::Pacman, name: :pacman_package, action: :install, os: "linux", ) end chef-12.3.0/spec/unit/resource/deploy_revision_spec.rb0000644000004100000410000000230312520074675023051 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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/resource/static_provider_resolution' describe Chef::Resource::DeployRevision do static_provider_resolution( resource: Chef::Resource::DeployRevision, provider: Chef::Provider::Deploy::Revision, name: :deploy_revision, action: :deploy, ) end describe Chef::Resource::DeployBranch do static_provider_resolution( resource: Chef::Resource::DeployBranch, provider: Chef::Provider::Deploy::Revision, name: :deploy_branch, action: :deploy, ) end chef-12.3.0/spec/unit/resource/resource_notification_spec.rb0000644000004100000410000001723012520074675024241 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/resource/resource_notification' describe Chef::Resource::Notification do before do @notification = Chef::Resource::Notification.new(:service_apache, :restart, :template_httpd_conf) end it "has a resource to be notified" do expect(@notification.resource).to eq(:service_apache) end it "has an action to take on the service" do expect(@notification.action).to eq(:restart) end it "has a notifying resource" do expect(@notification.notifying_resource).to eq(:template_httpd_conf) end it "is a duplicate of another notification with the same target resource and action" do other = Chef::Resource::Notification.new(:service_apache, :restart, :sync_web_app_code) expect(@notification.duplicates?(other)).to be_truthy end it "is not a duplicate of another notification if the actions differ" do other = Chef::Resource::Notification.new(:service_apache, :enable, :install_apache) expect(@notification.duplicates?(other)).to be_falsey end it "is not a duplicate of another notification if the target resources differ" do other = Chef::Resource::Notification.new(:service_sshd, :restart, :template_httpd_conf) expect(@notification.duplicates?(other)).to be_falsey end it "raises an ArgumentError if you try to check a non-ducktype object for duplication" do expect {@notification.duplicates?(:not_a_notification)}.to raise_error(ArgumentError) end it "takes no action to resolve a resource reference that doesn't need to be resolved" do @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @notification.resource = @keyboard_cat @long_cat = Chef::Resource::Cat.new("long_cat") @notification.notifying_resource = @long_cat @resource_collection = Chef::ResourceCollection.new # would raise an error since the resource is not in the collection @notification.resolve_resource_reference(@resource_collection) expect(@notification.resource).to eq(@keyboard_cat) end it "resolves a lazy reference to a resource" do @notification.resource = {:cat => "keyboard_cat"} @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @keyboard_cat @long_cat = Chef::Resource::Cat.new("long_cat") @notification.notifying_resource = @long_cat @notification.resolve_resource_reference(@resource_collection) expect(@notification.resource).to eq(@keyboard_cat) end it "resolves a lazy reference to its notifying resource" do @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @notification.resource = @keyboard_cat @notification.notifying_resource = {:cat => "long_cat"} @long_cat = Chef::Resource::Cat.new("long_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @long_cat @notification.resolve_resource_reference(@resource_collection) expect(@notification.notifying_resource).to eq(@long_cat) end it "resolves lazy references to both its resource and its notifying resource" do @notification.resource = {:cat => "keyboard_cat"} @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @keyboard_cat @notification.notifying_resource = {:cat => "long_cat"} @long_cat = Chef::Resource::Cat.new("long_cat") @resource_collection << @long_cat @notification.resolve_resource_reference(@resource_collection) expect(@notification.resource).to eq(@keyboard_cat) expect(@notification.notifying_resource).to eq(@long_cat) end it "raises a RuntimeError if you try to reference multiple resources" do @notification.resource = {:cat => ["keyboard_cat", "cheez_cat"]} @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @cheez_cat = Chef::Resource::Cat.new("cheez_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @keyboard_cat @resource_collection << @cheez_cat @long_cat = Chef::Resource::Cat.new("long_cat") @notification.notifying_resource = @long_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError) end it "raises a RuntimeError if you try to reference multiple notifying resources" do @notification.notifying_resource = {:cat => ["long_cat", "cheez_cat"]} @long_cat = Chef::Resource::Cat.new("long_cat") @cheez_cat = Chef::Resource::Cat.new("cheez_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @long_cat @resource_collection << @cheez_cat @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @notification.resource = @keyboard_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError) end it "raises a RuntimeError if it can't find a resource in the resource collection when resolving a lazy reference" do @notification.resource = {:cat => "keyboard_cat"} @cheez_cat = Chef::Resource::Cat.new("cheez_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @cheez_cat @long_cat = Chef::Resource::Cat.new("long_cat") @notification.notifying_resource = @long_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError) end it "raises a RuntimeError if it can't find a notifying resource in the resource collection when resolving a lazy reference" do @notification.notifying_resource = {:cat => "long_cat"} @cheez_cat = Chef::Resource::Cat.new("cheez_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @cheez_cat @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @notification.resource = @keyboard_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError) end it "raises an ArgumentError if improper syntax is used in the lazy reference to its resource" do @notification.resource = "cat => keyboard_cat" @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @keyboard_cat @long_cat = Chef::Resource::Cat.new("long_cat") @notification.notifying_resource = @long_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError) end it "raises an ArgumentError if improper syntax is used in the lazy reference to its notifying resource" do @notification.notifying_resource = "cat => long_cat" @long_cat = Chef::Resource::Cat.new("long_cat") @resource_collection = Chef::ResourceCollection.new @resource_collection << @long_cat @keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @notification.resource = @keyboard_cat expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError) end # Create test to resolve lazy references to both notifying resource and dest. resource # Create tests to check proper error raising end chef-12.3.0/spec/unit/resource/group_spec.rb0000644000004100000410000001100012520074675020765 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Tyler Cloke (); # 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' describe Chef::Resource::Group, "initialize" do before(:each) do @resource = Chef::Resource::Group.new("admin") end it "should create a new Chef::Resource::Group" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Group) end it "should set the resource_name to :group" do expect(@resource.resource_name).to eql(:group) end it "should set the group_name equal to the argument to initialize" do expect(@resource.group_name).to eql("admin") end it "should default gid to nil" do expect(@resource.gid).to eql(nil) end it "should default members to an empty array" do expect(@resource.members).to eql([]) end it "should alias users to members, also an empty array" do expect(@resource.users).to eql([]) end it "should set action to :create" do expect(@resource.action).to eql(:create) end %w{create remove modify manage}.each do |action| it "should allow action #{action}" do expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym) end end it "should accept domain groups (@ or \ separator) on non-windows" do expect { @resource.group_name "domain\@group" }.not_to raise_error expect(@resource.group_name).to eq("domain\@group") expect { @resource.group_name "domain\\group" }.not_to raise_error expect(@resource.group_name).to eq("domain\\group") expect { @resource.group_name "domain\\group^name" }.not_to raise_error expect(@resource.group_name).to eq("domain\\group^name") end end describe Chef::Resource::Group, "group_name" do before(:each) do @resource = Chef::Resource::Group.new("admin") end it "should allow a string" do @resource.group_name "pirates" expect(@resource.group_name).to eql("pirates") end it "should not allow a hash" do expect { @resource.send(:group_name, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError) end end describe Chef::Resource::Group, "gid" do before(:each) do @resource = Chef::Resource::Group.new("admin") end it "should allow an integer" do @resource.gid 100 expect(@resource.gid).to eql(100) end it "should not allow a hash" do expect { @resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError) end end describe Chef::Resource::Group, "members" do before(:each) do @resource = Chef::Resource::Group.new("admin") end [ :users, :members].each do |method| it "(#{method}) should allow and convert a string" do @resource.send(method, "aj") expect(@resource.send(method)).to eql(["aj"]) end it "(#{method}) should allow an array" do @resource.send(method, [ "aj", "adam" ]) expect(@resource.send(method)).to eql( ["aj", "adam"] ) end it "(#{method}) should not allow a hash" do expect { @resource.send(method, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError) end end end describe Chef::Resource::Group, "append" do before(:each) do @resource = Chef::Resource::Group.new("admin") end it "should default to false" do expect(@resource.append).to eql(false) end it "should allow a boolean" do @resource.append true expect(@resource.append).to eql(true) end it "should not allow a hash" do expect { @resource.send(:gid, { :aj => "is freakin awesome" }) }.to raise_error(ArgumentError) end describe "when it has members" do before do @resource.group_name("pokemon") @resource.members(["blastoise", "pikachu"]) end it "describes its state" do state = @resource.state expect(state[:members]).to eql(["blastoise", "pikachu"]) end it "returns the group name as its identity" do expect(@resource.identity).to eq("pokemon") end end end chef-12.3.0/spec/unit/resource/conditional_action_not_nothing_spec.rb0000644000004100000410000000256612520074675026120 0ustar www-datawww-data# # Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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::ConditionalActionNotNothing do describe "after running a :nothing action" do before do @action = :nothing @conditional = Chef::Resource::ConditionalActionNotNothing.new(@action) end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be_falsey end end describe "after running an action different to :nothing" do before do @action = :something @conditional = Chef::Resource::ConditionalActionNotNothing.new(@action) end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be_truthy end end end chef-12.3.0/spec/unit/resource/file/0000755000004100000410000000000012520074675017221 5ustar www-datawww-datachef-12.3.0/spec/unit/resource/file/verification_spec.rb0000644000004100000410000001010512520074675023237 0ustar www-datawww-data# # Author:: Steven Danna () # Copyright:: Copyright (c) 2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::File::Verification do let(:t_block) { Proc.new { true } } let(:f_block) { Proc.new { false } } let(:path_block) { Proc.new { |path| path }} let(:temp_path) { "/tmp/foobar" } describe "verification registration" do it "registers a verification for later use" do class Chef::Resource::File::Verification::Wombat < Chef::Resource::File::Verification provides :tabmow end expect(Chef::Resource::File::Verification.lookup(:tabmow)).to eq(Chef::Resource::File::Verification::Wombat) end it "raises an error if a verification can't be found" do expect{Chef::Resource::File::Verification.lookup(:dne)}.to raise_error(Chef::Exceptions::VerificationNotFound) end end describe "#verify" do let(:parent_resource) { Chef::Resource.new("llama") } it "expects a string argument" do v = Chef::Resource::File::Verification.new(parent_resource, nil, {}) {} expect{ v.verify("/foo/bar") }.to_not raise_error expect{ v.verify }.to raise_error end it "accepts an options hash" do v = Chef::Resource::File::Verification.new(parent_resource, nil, {}) {} expect{ v.verify("/foo/bar", {:future => true}) }.to_not raise_error end context "with a verification block" do it "passes a file path to the block" do v = Chef::Resource::File::Verification.new(parent_resource, nil, {}, &path_block) expect(v.verify(temp_path)).to eq(temp_path) end it "returns true if the block returned true" do v = Chef::Resource::File::Verification.new(parent_resource, nil, {}, &t_block) expect(v.verify(temp_path)).to eq(true) end it "returns false if the block returned false" do v = Chef::Resource::File::Verification.new(parent_resource, nil, {}, &f_block) expect(v.verify(temp_path)).to eq(false) end end context "with a verification command(String)" do it "substitutes \%{file} with the path" do test_command = if windows? "if \"#{temp_path}\" == \"%{file}\" (exit 0) else (exit 1)" else "test #{temp_path} = %{file}" end v = Chef::Resource::File::Verification.new(parent_resource, test_command, {}) expect(v.verify(temp_path)).to eq(true) end it "returns false if the command fails" do v = Chef::Resource::File::Verification.new(parent_resource, "false", {}) expect(v.verify(temp_path)).to eq(false) end it "returns true if the command succeeds" do v = Chef::Resource::File::Verification.new(parent_resource, "true", {}) expect(v.verify(temp_path)).to eq(true) end end context "with a named verification(Symbol)" do before(:each) do class Chef::Resource::File::Verification::Turtle < Chef::Resource::File::Verification provides :cats def verify(path, opts) end end end it "delegates to the registered verification" do registered_verification = double() allow(Chef::Resource::File::Verification::Turtle).to receive(:new).and_return(registered_verification) v = Chef::Resource::File::Verification.new(parent_resource, :cats, {}) expect(registered_verification).to receive(:verify).with(temp_path, {}) v.verify(temp_path, {}) end end end end chef-12.3.0/spec/unit/resource/bash_spec.rb0000644000004100000410000000226412520074675020562 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Bash do before(:each) do @resource = Chef::Resource::Bash.new("fakey_fakerton") end it "should create a new Chef::Resource::Bash" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Bash) end it "should have a resource name of :bash" do expect(@resource.resource_name).to eql(:bash) end it "should have an interpreter of bash" do expect(@resource.interpreter).to eql("bash") end end chef-12.3.0/spec/unit/resource/cron_spec.rb0000644000004100000410000001247312520074675020611 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Bryan McLellan # 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::Cron do before(:each) do @resource = Chef::Resource::Cron.new("cronify") end it "should create a new Chef::Resource::Cron" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Cron) end it "should have a name" do expect(@resource.name).to eql("cronify") end it "should have a default action of 'create'" do expect(@resource.action).to eql(:create) end it "should accept create or delete for action" do expect { @resource.action :create }.not_to raise_error expect { @resource.action :delete }.not_to raise_error expect { @resource.action :lolcat }.to raise_error(ArgumentError) end it "should allow you to set a command" do @resource.command "/bin/true" expect(@resource.command).to eql("/bin/true") end it "should allow you to set a user" do @resource.user "daemon" expect(@resource.user).to eql("daemon") end it "should allow you to specify the minute" do @resource.minute "30" expect(@resource.minute).to eql("30") end it "should allow you to specify the hour" do @resource.hour "6" expect(@resource.hour).to eql("6") end it "should allow you to specify the day" do @resource.day "10" expect(@resource.day).to eql("10") end it "should allow you to specify the month" do @resource.month "10" expect(@resource.month).to eql("10") end it "should allow you to specify the weekday" do @resource.weekday "2" expect(@resource.weekday).to eql("2") end it "should allow you to specify the mailto variable" do @resource.mailto "test@example.com" expect(@resource.mailto).to eql("test@example.com") end it "should allow you to specify the path" do @resource.path "/usr/bin:/usr/sbin" expect(@resource.path).to eql("/usr/bin:/usr/sbin") end it "should allow you to specify the home directory" do @resource.home "/root" expect(@resource.home).to eql("/root") end it "should allow you to specify the shell to run the command with" do @resource.shell "/bin/zsh" expect(@resource.shell).to eql("/bin/zsh") end it "should allow you to specify environment variables hash" do env = {"TEST" => "LOL"} @resource.environment env expect(@resource.environment).to eql(env) end it "should allow * for all time and date values" do [ "minute", "hour", "day", "month", "weekday" ].each do |x| expect(@resource.send(x, "*")).to eql("*") end end it "should allow ranges for all time and date values" do [ "minute", "hour", "day", "month", "weekday" ].each do |x| expect(@resource.send(x, "1-2,5")).to eql("1-2,5") end end it "should have a default value of * for all time and date values" do [ "minute", "hour", "day", "month", "weekday" ].each do |x| expect(@resource.send(x)).to eql("*") end end it "should have a default value of root for the user" do expect(@resource.user).to eql("root") end it "should reject any minute over 59" do expect { @resource.minute "60" }.to raise_error(RangeError) end it "should reject any hour over 23" do expect { @resource.hour "24" }.to raise_error(RangeError) end it "should reject any day over 31" do expect { @resource.day "32" }.to raise_error(RangeError) end it "should reject any month over 12" do expect { @resource.month "13" }.to raise_error(RangeError) end describe "weekday" do it "should reject any weekday over 7" do expect { @resource.weekday "8" }.to raise_error(RangeError) end it "should reject any symbols which don't represent day of week" do expect { @resource.weekday :foo }.to raise_error(RangeError) end end it "should convert integer schedule values to a string" do [ "minute", "hour", "day", "month", "weekday" ].each do |x| expect(@resource.send(x, 5)).to eql("5") end end describe "when it has a time (minute, hour, day, month, weeekend) and user" do before do @resource.command("tackle") @resource.minute("1") @resource.hour("2") @resource.day("3") @resource.month("4") @resource.weekday("5") @resource.user("root") end it "describes the state" do state = @resource.state expect(state[:minute]).to eq("1") expect(state[:hour]).to eq("2") expect(state[:day]).to eq("3") expect(state[:month]).to eq("4") expect(state[:weekday]).to eq("5") expect(state[:user]).to eq("root") end it "returns the command as its identity" do expect(@resource.identity).to eq("tackle") end end end chef-12.3.0/spec/unit/resource/service_spec.rb0000644000004100000410000001303512520074675021303 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Service do before(:each) do @resource = Chef::Resource::Service.new("chef") end it "should create a new Chef::Resource::Service" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Service) end it "should not set a provider unless node[:init_package] is defined as systemd" do expect(@resource.provider).to eq(nil) end it "should set the service_name to the first argument to new" do expect(@resource.service_name).to eql("chef") end it "should set the pattern to be the service name by default" do expect(@resource.pattern).to eql("chef") end it "should accept a string for the service name" do @resource.service_name "something" expect(@resource.service_name).to eql("something") end it "should accept a string for the service pattern" do @resource.pattern ".*" expect(@resource.pattern).to eql(".*") end it "should not accept a regexp for the service pattern" do expect { @resource.pattern /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service start command" do @resource.start_command "/etc/init.d/chef start" expect(@resource.start_command).to eql("/etc/init.d/chef start") end it "should not accept a regexp for the service start command" do expect { @resource.start_command /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service stop command" do @resource.stop_command "/etc/init.d/chef stop" expect(@resource.stop_command).to eql("/etc/init.d/chef stop") end it "should not accept a regexp for the service stop command" do expect { @resource.stop_command /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service status command" do @resource.status_command "/etc/init.d/chef status" expect(@resource.status_command).to eql("/etc/init.d/chef status") end it "should not accept a regexp for the service status command" do expect { @resource.status_command /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service restart command" do @resource.restart_command "/etc/init.d/chef restart" expect(@resource.restart_command).to eql("/etc/init.d/chef restart") end it "should not accept a regexp for the service restart command" do expect { @resource.restart_command /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service reload command" do @resource.reload_command "/etc/init.d/chef reload" expect(@resource.reload_command).to eql("/etc/init.d/chef reload") end it "should not accept a regexp for the service reload command" do expect { @resource.reload_command /.*/ }.to raise_error(ArgumentError) end it "should accept a string for the service init command" do @resource.init_command "/etc/init.d/chef" expect(@resource.init_command).to eql("/etc/init.d/chef") end it "should not accept a regexp for the service init command" do expect { @resource.init_command /.*/ }.to raise_error(ArgumentError) end %w{enabled running}.each do |attrib| it "should accept true for #{attrib}" do @resource.send(attrib, true) expect(@resource.send(attrib)).to eql(true) end it "should accept false for #{attrib}" do @resource.send(attrib, false) expect(@resource.send(attrib)).to eql(false) end it "should not accept a string for #{attrib}" do expect { @resource.send(attrib, "poop") }.to raise_error(ArgumentError) end it "should default all the feature support to false" do support_hash = { :status => false, :restart => false, :reload=> false } expect(@resource.supports).to eq(support_hash) end it "should allow you to set what features this resource supports as a array" do support_array = [ :status, :restart ] support_hash = { :status => true, :restart => true, :reload => false } @resource.supports(support_array) expect(@resource.supports).to eq(support_hash) end it "should allow you to set what features this resource supports as a hash" do support_hash = { :status => true, :restart => true, :reload => false } @resource.supports(support_hash) expect(@resource.supports).to eq(support_hash) end end describe "when it has pattern and supports" do before do @resource.service_name("superfriend") @resource.enabled(true) @resource.running(false) end it "describes its state" do state = @resource.state expect(state[:enabled]).to eql(true) expect(state[:running]).to eql(false) end it "returns the service name as its identity" do expect(@resource.identity).to eq("superfriend") end end end chef-12.3.0/spec/unit/resource/ips_package_spec.rb0000644000004100000410000000232412520074675022110 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::IpsPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::IpsPackage, provider: Chef::Provider::Package::Ips, name: :ips_package, action: :install, os: "solaris2", ) before(:each) do @resource = Chef::Resource::IpsPackage.new("crypto/gnupg") end it "should support accept_license" do @resource.accept_license(true) expect(@resource.accept_license).to eql(true) end end chef-12.3.0/spec/unit/resource/macports_package_spec.rb0000644000004100000410000000202212520074675023140 0ustar www-datawww-data# # Author:: David Balatero () # 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 'spec_helper' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::MacportsPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::MacportsPackage, provider: Chef::Provider::Package::Macports, name: :macports_package, action: :install, os: "mac_os_x", ) end chef-12.3.0/spec/unit/resource/subversion_spec.rb0000644000004100000410000000416112520074675022042 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Subversion do static_provider_resolution( resource: Chef::Resource::Subversion, provider: Chef::Provider::Subversion, name: :subversion, action: :install, ) before do @svn = Chef::Resource::Subversion.new("ohai, svn project!") end it "is a subclass of Resource::Scm" do expect(@svn).to be_an_instance_of(Chef::Resource::Subversion) expect(@svn).to be_a_kind_of(Chef::Resource::Scm) end it "allows the force_export action" do expect(@svn.allowed_actions).to include(:force_export) end it "sets svn info arguments to --no-auth-cache by default" do expect(@svn.svn_info_args).to eq('--no-auth-cache') end it "resets svn info arguments to nil when given false in the setter" do @svn.svn_info_args(false) expect(@svn.svn_info_args).to be_nil end it "sets svn arguments to --no-auth-cache by default" do expect(@svn.svn_arguments).to eq('--no-auth-cache') end it "resets svn arguments to nil when given false in the setter" do @svn.svn_arguments(false) expect(@svn.svn_arguments).to be_nil end it "hides password from custom exception message" do @svn.svn_password "l33th4x0rpa$$w0rd" e = @svn.customize_exception(Chef::Exceptions::Exec.new "Exception with password #{@svn.svn_password}") expect(e.message.include?(@svn.svn_password)).to be_falsey end end chef-12.3.0/spec/unit/resource/link_spec.rb0000644000004100000410000001042712520074675020602 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Link do before(:each) do expect_any_instance_of(Chef::Resource::Link).to receive(:verify_links_supported!).and_return(true) @resource = Chef::Resource::Link.new("fakey_fakerton") end it "should create a new Chef::Resource::Link" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Link) end it "should have a name" do expect(@resource.name).to eql("fakey_fakerton") end it "should have a default action of 'create'" do expect(@resource.action).to eql(:create) end { :create => false, :delete => false, :blues => true }.each do |action,bad_value| it "should #{bad_value ? 'not' : ''} accept #{action.to_s}" do if bad_value expect { @resource.action action }.to raise_error(ArgumentError) else expect { @resource.action action }.not_to raise_error end end end it "should use the object name as the target_file by default" do expect(@resource.target_file).to eql("fakey_fakerton") end it "should accept a delayed evaluator as the target path" do @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" } expect(@resource.target_file).to eql("my_lazy_name") end it "should accept a delayed evaluator when accessing via 'path'" do @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" } expect(@resource.path).to eql("my_lazy_name") end it "should accept a delayed evaluator via 'to'" do @resource.to Chef::DelayedEvaluator.new { "my_lazy_name" } expect(@resource.to).to eql("my_lazy_name") end it "should accept a string as the link source via 'to'" do expect { @resource.to "/tmp" }.not_to raise_error end it "should not accept a Hash for the link source via 'to'" do expect { @resource.to Hash.new }.to raise_error(ArgumentError) end it "should allow you to set a link source via 'to'" do @resource.to "/tmp/foo" expect(@resource.to).to eql("/tmp/foo") end it "should allow you to specify the link type" do @resource.link_type "symbolic" expect(@resource.link_type).to eql(:symbolic) end it "should default to a symbolic link" do expect(@resource.link_type).to eql(:symbolic) end it "should accept a hard link_type" do @resource.link_type :hard expect(@resource.link_type).to eql(:hard) end it "should reject any other link_type but :hard and :symbolic" do expect { @resource.link_type "x-men" }.to raise_error(ArgumentError) end it "should accept a group name or id for group" do expect { @resource.group "root" }.not_to raise_error expect { @resource.group 123 }.not_to raise_error expect { @resource.group "root:goo" }.to raise_error(ArgumentError) end it "should accept a user name or id for owner" do expect { @resource.owner "root" }.not_to raise_error expect { @resource.owner 123 }.not_to raise_error expect { @resource.owner "root:goo" }.to raise_error(ArgumentError) end describe "when it has to, link_type, owner, and group" do before do @resource.target_file("/var/target.tar") @resource.to("/to/dir/file.tar") @resource.link_type(:symbolic) @resource.owner("root") @resource.group("0664") end it "describes its state" do state = @resource.state expect(state[:to]).to eq("/to/dir/file.tar") expect(state[:owner]).to eq("root") expect(state[:group]).to eq("0664") end it "returns the target file as its identity" do expect(@resource.identity).to eq("/var/target.tar") end end end chef-12.3.0/spec/unit/resource/windows_service_spec.rb0000644000004100000410000000304112520074675023051 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::WindowsService, "initialize" do static_provider_resolution( resource: Chef::Resource::WindowsService, provider: Chef::Provider::Service::Windows, os: "windows", name: :windows_service, action: :start ) let(:resource) { Chef::Resource::WindowsService.new("BITS") } it "returns a Chef::Resource::WindowsService" do expect(resource).to be_a_kind_of(Chef::Resource::WindowsService) end it "sets the resource_name to :windows_service" do expect(resource.resource_name).to eql(:windows_service) end it "supports setting startup_type" do resource.startup_type(:manual) expect(resource.startup_type).to eql(:manual) end it "allows the action to be 'configure_startup'" do resource.action :configure_startup expect(resource.action).to eq([:configure_startup]) end end chef-12.3.0/spec/unit/resource/gem_package_spec.rb0000644000004100000410000000245312520074675022070 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::GemPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::GemPackage, provider: Chef::Provider::Package::Rubygems, name: :gem_package, action: :install, ) end describe Chef::Resource::GemPackage, "gem_binary" do before(:each) do @resource = Chef::Resource::GemPackage.new("foo") end it "should set the gem_binary variable to whatever is passed in" do @resource.gem_binary("/opt/local/bin/gem") expect(@resource.gem_binary).to eql("/opt/local/bin/gem") end end chef-12.3.0/spec/unit/resource/execute_spec.rb0000644000004100000410000000230412520074675021302 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Execute do let(:resource_instance_name) { "some command" } let(:execute_resource) { Chef::Resource::Execute.new(resource_instance_name) } it_behaves_like "an execute resource" it "default guard interpreter should be :execute interpreter" do expect(execute_resource.guard_interpreter).to be(:execute) end it "defaults to not being a guard interpreter" do expect(execute_resource.is_guard_interpreter).to eq(false) end end chef-12.3.0/spec/unit/resource/mount_spec.rb0000644000004100000410000001453012520074675021006 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Tyler Cloke () # 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 'spec_helper' describe Chef::Resource::Mount do before(:each) do @resource = Chef::Resource::Mount.new("filesystem") end it "should create a new Chef::Resource::Mount" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Mount) end it "should have a name" do expect(@resource.name).to eql("filesystem") end it "should set mount_point to the name" do expect(@resource.mount_point).to eql("filesystem") end it "should have a default action of mount" do expect(@resource.action).to eql(:mount) end it "should accept mount, umount and remount as actions" do expect { @resource.action :mount }.not_to raise_error expect { @resource.action :umount }.not_to raise_error expect { @resource.action :remount }.not_to raise_error expect { @resource.action :brooklyn }.to raise_error(ArgumentError) end it "should allow you to set the device attribute" do @resource.device "/dev/sdb3" expect(@resource.device).to eql("/dev/sdb3") end it "should set fsck_device to '-' by default" do expect(@resource.fsck_device).to eql('-') end it "should allow you to set the fsck_device attribute" do @resource.fsck_device "/dev/rdsk/sdb3" expect(@resource.fsck_device).to eql("/dev/rdsk/sdb3") end it "should allow you to set the fstype attribute" do @resource.fstype "nfs" expect(@resource.fstype).to eql("nfs") end it "should allow you to set the dump attribute" do @resource.dump 1 expect(@resource.dump).to eql(1) end it "should allow you to set the pass attribute" do @resource.pass 1 expect(@resource.pass).to eql(1) end it "should set the options attribute to defaults" do expect(@resource.options).to eql(["defaults"]) end it "should allow options to be sent as a string, and convert to array" do @resource.options "rw,noexec" expect(@resource.options).to be_a_kind_of(Array) end it "should allow options attribute as an array" do @resource.options ["ro", "nosuid"] expect(@resource.options).to be_a_kind_of(Array) end it "should allow options to be sent as a delayed evaluator" do @resource.options Chef::DelayedEvaluator.new {["rw", "noexec"]} expect(@resource.options).to eql(["rw", "noexec"]) end it "should allow options to be sent as a delayed evaluator, and convert to array" do @resource.options Chef::DelayedEvaluator.new {"rw,noexec"} expect(@resource.options).to be_a_kind_of(Array) expect(@resource.options).to eql(["rw", "noexec"]) end it "should accept true for mounted" do @resource.mounted(true) expect(@resource.mounted).to eql(true) end it "should accept false for mounted" do @resource.mounted(false) expect(@resource.mounted).to eql(false) end it "should set mounted to false by default" do expect(@resource.mounted).to eql(false) end it "should not accept a string for mounted" do expect { @resource.mounted("poop") }.to raise_error(ArgumentError) end it "should accept true for enabled" do @resource.enabled(true) expect(@resource.enabled).to eql(true) end it "should accept false for enabled" do @resource.enabled(false) expect(@resource.enabled).to eql(false) end it "should set enabled to false by default" do expect(@resource.enabled).to eql(false) end it "should not accept a string for enabled" do expect { @resource.enabled("poop") }.to raise_error(ArgumentError) end it "should default all feature support to false" do support_hash = { :remount => false } expect(@resource.supports).to eq(support_hash) end it "should allow you to set feature support as an array" do support_array = [ :remount ] support_hash = { :remount => true } @resource.supports(support_array) expect(@resource.supports).to eq(support_hash) end it "should allow you to set feature support as a hash" do support_hash = { :remount => true } @resource.supports(support_hash) expect(@resource.supports).to eq(support_hash) end it "should allow you to set username" do @resource.username("Administrator") expect(@resource.username).to eq("Administrator") end it "should allow you to set password" do @resource.password("Jetstream123!") expect(@resource.password).to eq("Jetstream123!") end it "should allow you to set domain" do @resource.domain("TEST_DOMAIN") expect(@resource.domain).to eq("TEST_DOMAIN") end describe "when it has mount point, device type, and fstype" do before do @resource.device("charmander") @resource.mount_point("123.456") @resource.device_type(:device) @resource.fstype("ranked") end it "describes its state" do state = @resource.state expect(state[:mount_point]).to eq("123.456") expect(state[:device_type]).to eql(:device) expect(state[:fstype]).to eq("ranked") end it "returns the device as its identity" do expect(@resource.identity).to eq("charmander") end end describe "when it has username, password and domain" do before do @resource.mount_point("T:") @resource.device("charmander") @resource.username("Administrator") @resource.password("Jetstream123!") @resource.domain("TEST_DOMAIN") end it "describes its state" do state = @resource.state expect(state[:mount_point]).to eq("T:") expect(state[:username]).to eq("Administrator") expect(state[:password]).to eq("Jetstream123!") expect(state[:domain]).to eq("TEST_DOMAIN") expect(state[:device_type]).to eql(:device) expect(state[:fstype]).to eq("auto") end end end chef-12.3.0/spec/unit/resource/chef_gem_spec.rb0000644000004100000410000001145212520074675021401 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Bryan McLellan # Copyright:: Copyright (c) 2008, 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 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::ChefGem, "initialize" do static_provider_resolution( resource: Chef::Resource::ChefGem, provider: Chef::Provider::Package::Rubygems, name: :chef_gem, action: :install, ) end describe Chef::Resource::ChefGem, "gem_binary" do let(:resource) { Chef::Resource::ChefGem.new("foo") } before(:each) do expect(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/chef/embedded/bin") end it "should raise an exception when gem_binary is set" do expect { resource.gem_binary("/lol/cats/gem") }.to raise_error(ArgumentError) end it "should set the gem_binary based on computing it from RbConfig" do expect(resource.gem_binary).to eql("/opt/chef/embedded/bin/gem") end it "should set the gem_binary based on computing it from RbConfig" do expect(resource.compile_time).to be nil end context "when building the resource" do let(:node) do Chef::Node.new.tap {|n| n.normal[:tags] = [] } end let(:run_context) do Chef::RunContext.new(node, {}, nil) end let(:recipe) do Chef::Recipe.new("hjk", "test", run_context) end let(:chef_gem_compile_time) { nil } let(:resource) do Chef::Config[:chef_gem_compile_time] = chef_gem_compile_time Chef::Resource::ChefGem.new("foo", run_context) end before do expect(Chef::Resource::ChefGem).to receive(:new).and_return(resource) end it "runs the install at compile-time by default", :chef_lt_13_only do expect(resource).to receive(:run_action).with(:install) expect(Chef::Log).to receive(:deprecation).at_least(:once) recipe.chef_gem "foo" end # the default behavior will change in Chef-13 it "does not runs the install at compile-time by default", :chef_gte_13_only do expect(resource).not_to receive(:run_action).with(:install) expect(Chef::Log).not_to receive(:deprecation) recipe.chef_gem "foo" end it "compile_time true installs at compile-time" do expect(resource).to receive(:run_action).with(:install) expect(Chef::Log).not_to receive(:deprecation) recipe.chef_gem "foo" do compile_time true end end it "compile_time false does not install at compile-time" do expect(resource).not_to receive(:run_action).with(:install) expect(Chef::Log).not_to receive(:deprecation) recipe.chef_gem "foo" do compile_time false end end describe "when Chef::Config[:chef_gem_compile_time] is explicitly true" do let(:chef_gem_compile_time) { true } before do expect(Chef::Log).not_to receive(:deprecation) end it "by default installs at compile-time" do expect(resource).to receive(:run_action).with(:install) recipe.chef_gem "foo" end it "compile_time true installs at compile-time" do expect(resource).to receive(:run_action).with(:install) recipe.chef_gem "foo" do compile_time true end end it "compile_time false does not install at compile-time" do expect(resource).not_to receive(:run_action).with(:install) recipe.chef_gem "foo" do compile_time false end end end describe "when Chef::Config[:chef_gem_compile_time] is explicitly false" do let(:chef_gem_compile_time) { false } before do expect(Chef::Log).not_to receive(:deprecation) end it "by default does not install at compile-time" do expect(resource).not_to receive(:run_action).with(:install) recipe.chef_gem "foo" end it "compile_time true installs at compile-time" do expect(resource).to receive(:run_action).with(:install) recipe.chef_gem "foo" do compile_time true end end it "compile_time false does not install at compile-time" do expect(resource).not_to receive(:run_action).with(:install) recipe.chef_gem "foo" do compile_time false end end end end end chef-12.3.0/spec/unit/resource/ruby_spec.rb0000644000004100000410000000226412520074675020626 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Ruby do before(:each) do @resource = Chef::Resource::Ruby.new("fakey_fakerton") end it "should create a new Chef::Resource::Ruby" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Ruby) end it "should have a resource name of :ruby" do expect(@resource.resource_name).to eql(:ruby) end it "should have an interpreter of ruby" do expect(@resource.interpreter).to eql("ruby") end end chef-12.3.0/spec/unit/resource/env_spec.rb0000644000004100000410000000466612520074675020445 0ustar www-datawww-data# # Author:: Doug MacEachern () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2010 VMware, 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::Env do before(:each) do @resource = Chef::Resource::Env.new("FOO") end it "should create a new Chef::Resource::Env" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Env) end it "should have a name" do expect(@resource.name).to eql("FOO") end it "should have a default action of 'create'" do expect(@resource.action).to eql(:create) end { :create => false, :delete => false, :modify => false, :flibber => true }.each do |action,bad_value| it "should #{bad_value ? 'not' : ''} accept #{action.to_s}" do if bad_value expect { @resource.action action }.to raise_error(ArgumentError) else expect { @resource.action action }.not_to raise_error end end end it "should use the object name as the key_name by default" do expect(@resource.key_name).to eql("FOO") end it "should accept a string as the env value via 'value'" do expect { @resource.value "bar" }.not_to raise_error end it "should not accept a Hash for the env value via 'to'" do expect { @resource.value Hash.new }.to raise_error(ArgumentError) end it "should allow you to set an env value via 'to'" do @resource.value "bar" expect(@resource.value).to eql("bar") end describe "when it has key name and value" do before do @resource.key_name("charmander") @resource.value("level7") @resource.delim("hi") end it "describes its state" do state = @resource.state expect(state[:value]).to eq("level7") end it "returns the key name as its identity" do expect(@resource.identity).to eq("charmander") end end end chef-12.3.0/spec/unit/resource/windows_package_spec.rb0000644000004100000410000000476212520074675023017 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::WindowsPackage, "initialize" do before(:each) do stub_const("File::ALT_SEPARATOR", "\\") end static_provider_resolution( resource: Chef::Resource::WindowsPackage, provider: Chef::Provider::Package::Windows, os: "windows", name: :windows_package, action: :start ) let(:resource) { Chef::Resource::WindowsPackage.new("solitaire.msi") } it "returns a Chef::Resource::WindowsPackage" do expect(resource).to be_a_kind_of(Chef::Resource::WindowsPackage) end it "sets the resource_name to :windows_package" do expect(resource.resource_name).to eql(:windows_package) end it "supports setting installer_type as a symbol" do resource.installer_type(:msi) expect(resource.installer_type).to eql(:msi) end # String, Integer [ "600", 600 ].each do |val| it "supports setting a timeout as a #{val.class}" do resource.timeout(val) expect(resource.timeout).to eql(val) end end # String, Integer, Array [ "42", 42, [47, 48, 49] ].each do |val| it "supports setting an alternate return value as a #{val.class}" do resource.returns(val) expect(resource.returns).to eql(val) end end it "coverts a source to an absolute path" do allow(::File).to receive(:absolute_path).and_return("c:\\Files\\frost.msi") resource.source("frost.msi") expect(resource.source).to eql "c:\\Files\\frost.msi" end it "converts slashes to backslashes in the source path" do allow(::File).to receive(:absolute_path).and_return("c:\\frost.msi") resource.source("c:/frost.msi") expect(resource.source).to eql "c:\\frost.msi" end it "defaults source to the resource name" do # it's a little late to stub out File.absolute_path expect(resource.source).to include("solitaire.msi") end end chef-12.3.0/spec/unit/resource/script_spec.rb0000644000004100000410000000266112520074675021152 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' 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 } it "should accept a string for the interpreter" do script_resource.interpreter "naaaaNaNaNaaNaaNaaNaa" expect(script_resource.interpreter).to eql("naaaaNaNaNaaNaaNaaNaa") end context "when it has interpreter and flags" do before do script_resource.interpreter("gcc") script_resource.flags("-al") end it "returns the name as its identity" do expect(script_resource.identity).to eq(resource_instance_name) end end it_behaves_like "a script resource" end chef-12.3.0/spec/unit/resource/ifconfig_spec.rb0000644000004100000410000000726112520074675021433 0ustar www-datawww-data# # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Joe Williams # 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::Ifconfig do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @resource = Chef::Resource::Ifconfig.new("fakey_fakerton", @run_context) end describe "when it has target, hardware address, inet address, and a mask" do before do @resource.device("charmander") @resource.target("team_rocket") @resource.hwaddr("11.2223.223") @resource.inet_addr("434.2343.23") @resource.mask("255.255.545") end it "describes its state" do state = @resource.state expect(state[:inet_addr]).to eq("434.2343.23") expect(state[:mask]).to eq("255.255.545") end it "returns the device as its identity" do expect(@resource.identity).to eq("charmander") end end shared_examples "being a platform using the default ifconfig provider" do |platform, version| before do @node.automatic_attrs[:platform] = platform @node.automatic_attrs[:platform_version] = version end it "should use an ordinary Provider::Ifconfig as a provider for #{platform} #{version}" do expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig) expect(@resource.provider_for_action(:add)).not_to be_a_kind_of(Chef::Provider::Ifconfig::Debian) expect(@resource.provider_for_action(:add)).not_to be_a_kind_of(Chef::Provider::Ifconfig::Redhat) end end shared_examples "being a platform based on RedHat" do |platform, version| before do @node.automatic_attrs[:platform] = platform @node.automatic_attrs[:platform_version] = version end it "should use an Provider::Ifconfig::Redhat as a provider for #{platform} #{version}" do expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig::Redhat) end end shared_examples "being a platform based on a recent Debian" do |platform, version| before do @node.automatic_attrs[:platform] = platform @node.automatic_attrs[:platform_version] = version end it "should use an Ifconfig::Debian as a provider for #{platform} #{version}" do expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig::Debian) end end describe "when it is a RedHat platform" do it_should_behave_like "being a platform based on RedHat", "redhat", "4.0" end describe "when it is an old Debian platform" do it_should_behave_like "being a platform using the default ifconfig provider", "debian", "6.0" end describe "when it is a new Debian platform" do it_should_behave_like "being a platform based on a recent Debian", "debian", "7.0" end describe "when it is an old Ubuntu platform" do it_should_behave_like "being a platform using the default ifconfig provider", "ubuntu", "11.04" end describe "when it is a new Ubuntu platform" do it_should_behave_like "being a platform based on a recent Debian", "ubuntu", "11.10" end end chef-12.3.0/spec/unit/resource/rpm_package_spec.rb0000644000004100000410000000274012520074675022115 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2010 Thomas Bishop # 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/resource/static_provider_resolution' describe Chef::Resource::RpmPackage, "initialize" do %w{linux aix}.each do |os| static_provider_resolution( resource: Chef::Resource::RpmPackage, provider: Chef::Provider::Package::Rpm, name: :rpm_package, action: :install, os: os ) end end describe Chef::Resource::RpmPackage, "allow_downgrade" do before(:each) do @resource = Chef::Resource::RpmPackage.new("foo") end it "should allow you to specify whether allow_downgrade is true or false" do expect { @resource.allow_downgrade true }.not_to raise_error expect { @resource.allow_downgrade false }.not_to raise_error expect { @resource.allow_downgrade "monkey" }.to raise_error(ArgumentError) end end chef-12.3.0/spec/unit/resource/scm_spec.rb0000644000004100000410000001337312520074675020432 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Scm do before(:each) do @resource = Chef::Resource::Scm.new("my awesome app") end it "should be a SCM resource" do expect(@resource).to be_a_kind_of(Chef::Resource::Scm) end it "supports :checkout, :export, :sync, :diff, and :log actions" do expect(@resource.allowed_actions).to include(:checkout) expect(@resource.allowed_actions).to include(:export) expect(@resource.allowed_actions).to include(:sync) expect(@resource.allowed_actions).to include(:diff) expect(@resource.allowed_actions).to include(:log) end it "takes the destination path as a string" do @resource.destination "/path/to/deploy/dir" expect(@resource.destination).to eql("/path/to/deploy/dir") end it "takes a string for the repository URL" do @resource.repository "git://github.com/opscode/chef.git" expect(@resource.repository).to eql("git://github.com/opscode/chef.git") end it "takes a string for the revision" do @resource.revision "abcdef" expect(@resource.revision).to eql("abcdef") end it "defaults to the ``HEAD'' revision" do expect(@resource.revision).to eql("HEAD") end it "takes a string for the user to run as" do @resource.user "dr_deploy" expect(@resource.user).to eql("dr_deploy") end it "also takes an integer for the user to run as" do @resource.user 0 expect(@resource.user).to eql(0) end it "takes a string for the group to run as, defaulting to nil" do expect(@resource.group).to be_nil @resource.group "opsdevs" expect(@resource.group).to eq("opsdevs") end it "also takes an integer for the group to run as" do @resource.group 23 expect(@resource.group).to eq(23) end it "has a svn_username String attribute" do @resource.svn_username "moartestsplz" expect(@resource.svn_username).to eql("moartestsplz") end it "has a svn_password String attribute" do @resource.svn_password "taftplz" expect(@resource.svn_password).to eql("taftplz") end it "has a svn_arguments String attribute" do @resource.svn_arguments "--more-taft plz" expect(@resource.svn_arguments).to eql("--more-taft plz") end it "has a svn_info_args String attribute" do expect(@resource.svn_info_args).to be_nil @resource.svn_info_args("--no-moar-plaintext-creds yep") expect(@resource.svn_info_args).to eq("--no-moar-plaintext-creds yep") end it "takes the depth as an integer for shallow clones" do @resource.depth 5 expect(@resource.depth).to eq(5) expect {@resource.depth "five"}.to raise_error(ArgumentError) end it "defaults to nil depth for a full clone" do expect(@resource.depth).to be_nil end it "takes a boolean for #enable_submodules" do @resource.enable_submodules true expect(@resource.enable_submodules).to be_truthy expect {@resource.enable_submodules "lolz"}.to raise_error(ArgumentError) end it "defaults to not enabling submodules" do expect(@resource.enable_submodules).to be_falsey end it "takes a boolean for #enable_checkout" do @resource.enable_checkout true expect(@resource.enable_checkout).to be_truthy expect {@resource.enable_checkout "lolz"}.to raise_error(ArgumentError) end it "defaults to enabling checkout" do expect(@resource.enable_checkout).to be_truthy end it "takes a string for the remote" do @resource.remote "opscode" expect(@resource.remote).to eql("opscode") expect {@resource.remote 1337}.to raise_error(ArgumentError) end it "defaults to ``origin'' for the remote" do expect(@resource.remote).to eq("origin") end it "takes a string for the ssh wrapper" do @resource.ssh_wrapper "with_ssh_fu" expect(@resource.ssh_wrapper).to eql("with_ssh_fu") end it "defaults to nil for the ssh wrapper" do expect(@resource.ssh_wrapper).to be_nil end it "defaults to nil for the environment" do expect(@resource.environment).to be_nil end describe "when it has a timeout attribute" do let(:ten_seconds) { 10 } before { @resource.timeout(ten_seconds) } it "stores this timeout" do expect(@resource.timeout).to eq(ten_seconds) end end describe "when it has no timeout attribute" do it "should have no default timeout" do expect(@resource.timeout).to be_nil end end describe "when it has repository, revision, user, and group" do before do @resource.destination("hell") @resource.repository("apt") @resource.revision("1.2.3") @resource.user("root") @resource.group("super_adventure_club") end it "describes its state" do state = @resource.state expect(state[:revision]).to eq("1.2.3") end it "returns the destination as its identity" do expect(@resource.identity).to eq("hell") end end describe "when it has a environment attribute" do let(:test_environment) { {'CHEF_ENV' => '/tmp' } } before { @resource.environment(test_environment) } it "stores this environment" do expect(@resource.environment).to eq(test_environment) end end end chef-12.3.0/spec/unit/resource/package_spec.rb0000644000004100000410000000531412520074675021237 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Package do before(:each) do @resource = Chef::Resource::Package.new("emacs") end it "should create a new Chef::Resource::Package" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Package) end it "should set the package_name to the first argument to new" do expect(@resource.package_name).to eql("emacs") end it "should accept a string for the package name" do @resource.package_name "something" expect(@resource.package_name).to eql("something") end it "should accept a string for the version" do @resource.version "something" expect(@resource.version).to eql("something") end it "should accept a string for the response file" do @resource.response_file "something" expect(@resource.response_file).to eql("something") end it "should accept a hash for response file template variables" do @resource.response_file_variables({:variables => true}) expect(@resource.response_file_variables).to eql({:variables => true}) end it "should accept a string for the source" do @resource.source "something" expect(@resource.source).to eql("something") end it "should accept a string for the options" do @resource.options "something" expect(@resource.options).to eql("something") end describe "when it has a package_name and version" do before do @resource.package_name("tomcat") @resource.version("10.9.8") @resource.options("-al") end it "describes its state" do state = @resource.state expect(state[:version]).to eq("10.9.8") expect(state[:options]).to eq("-al") end it "returns the file path as its identity" do expect(@resource.identity).to eq("tomcat") end end # String, Integer [ "600", 600 ].each do |val| it "supports setting a timeout as a #{val.class}" do @resource.timeout(val) expect(@resource.timeout).to eql(val) end end end chef-12.3.0/spec/unit/resource/apt_package_spec.rb0000644000004100000410000000231612520074675022102 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::AptPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::AptPackage, provider: Chef::Provider::Package::Apt, name: :apt_package, action: :install, os: "linux", ) let(:resource) { Chef::Resource::AptPackage.new("foo") } it "should support default_release" do resource.default_release("lenny-backports") expect(resource.default_release).to eql("lenny-backports") end end chef-12.3.0/spec/unit/resource/cookbook_file_spec.rb0000644000004100000410000000577512520074675022464 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2010 Opscode, Inc. #p 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::CookbookFile do before do @cookbook_file = Chef::Resource::CookbookFile.new('sourcecode_tarball.tgz') end it "uses the name parameter for the source parameter" do expect(@cookbook_file.name).to eq('sourcecode_tarball.tgz') end it "has a source parameter" do @cookbook_file.name('config_file.conf') expect(@cookbook_file.name).to eq('config_file.conf') end it "defaults to a nil cookbook parameter (current cookbook will be used)" do expect(@cookbook_file.cookbook).to be_nil end it "has a cookbook parameter" do @cookbook_file.cookbook("munin") expect(@cookbook_file.cookbook).to eq('munin') end it "sets the provider to Chef::Provider::CookbookFile" do expect(@cookbook_file.provider).to eq(Chef::Provider::CookbookFile) end describe "when it has a backup number, group, mode, owner, source, checksum, and cookbook on nix or path, rights, deny_rights, checksum on windows" do before do if Chef::Platform.windows? @cookbook_file.path("C:/temp/origin/file.txt") @cookbook_file.rights(:read, "Everyone") @cookbook_file.deny_rights(:full_control, "Clumsy_Sam") else @cookbook_file.path("/tmp/origin/file.txt") @cookbook_file.group("wheel") @cookbook_file.mode("0664") @cookbook_file.owner("root") @cookbook_file.source("/tmp/foo.txt") @cookbook_file.cookbook("/tmp/cookbooks/cooked.rb") end @cookbook_file.checksum("1" * 64) end it "describes the state" do state = @cookbook_file.state if Chef::Platform.windows? puts state expect(state[:rights]).to eq([{:permissions => :read, :principals => "Everyone"}]) expect(state[:deny_rights]).to eq([{:permissions => :full_control, :principals => "Clumsy_Sam"}]) else expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0664") expect(state[:owner]).to eq("root") end expect(state[:checksum]).to eq("1" * 64) end it "returns the path as its identity" do if Chef::Platform.windows? expect(@cookbook_file.identity).to eq("C:/temp/origin/file.txt") else expect(@cookbook_file.identity).to eq("/tmp/origin/file.txt") end end end end chef-12.3.0/spec/unit/resource/user_spec.rb0000644000004100000410000000746412520074675020632 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::User, "initialize" do before(:each) do @resource = Chef::Resource::User.new("adam") end it "should create a new Chef::Resource::User" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::User) end it "should set the resource_name to :user" do expect(@resource.resource_name).to eql(:user) end it "should set the username equal to the argument to initialize" do expect(@resource.username).to eql("adam") end %w{comment uid gid home shell password}.each do |attrib| it "should set #{attrib} to nil" do expect(@resource.send(attrib)).to eql(nil) end end it "should set action to :create" do expect(@resource.action).to eql(:create) end it "should set supports[:manage_home] to false" do expect(@resource.supports[:manage_home]).to eql(false) end it "should set supports[:non_unique] to false" do expect(@resource.supports[:non_unique]).to eql(false) end it "should set force to false" do expect(@resource.force).to eql(false) end %w{create remove modify manage lock unlock}.each do |action| it "should allow action #{action}" do expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym) end end it "should accept domain users (@ or \ separator) on non-windows" do expect { @resource.username "domain\@user" }.not_to raise_error expect(@resource.username).to eq("domain\@user") expect { @resource.username "domain\\user" }.not_to raise_error expect(@resource.username).to eq("domain\\user") end end %w{username comment home shell password}.each do |attrib| describe Chef::Resource::User, attrib do before(:each) do @resource = Chef::Resource::User.new("adam") end it "should allow a string" do @resource.send(attrib, "adam") expect(@resource.send(attrib)).to eql("adam") end it "should not allow a hash" do expect { @resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError) end end end %w{uid gid}.each do |attrib| describe Chef::Resource::User, attrib do before(:each) do @resource = Chef::Resource::User.new("adam") end it "should allow a string" do @resource.send(attrib, "100") expect(@resource.send(attrib)).to eql("100") end it "should allow an integer" do @resource.send(attrib, 100) expect(@resource.send(attrib)).to eql(100) end it "should not allow a hash" do expect { @resource.send(attrib, { :woot => "i found it" }) }.to raise_error(ArgumentError) end end describe "when it has uid, gid, and home" do before do @resource = Chef::Resource::User.new("root") @resource.uid(123) @resource.gid(456) @resource.home("/usr/local/root/") end it "describes its state" do state = @resource.state expect(state[:uid]).to eq(123) expect(state[:gid]).to eq(456) expect(state[:home]).to eq("/usr/local/root/") end it "returns the username as its identity" do expect(@resource.identity).to eq("root") end end end chef-12.3.0/spec/unit/resource/directory_spec.rb0000644000004100000410000000513612520074675021652 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Directory do before(:each) do @resource = Chef::Resource::Directory.new("fakey_fakerton") end it "should create a new Chef::Resource::Directory" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Directory) end it "should have a name" do expect(@resource.name).to eql("fakey_fakerton") end it "should have a default action of 'create'" do expect(@resource.action).to eql(:create) end it "should accept create or delete for action" do expect { @resource.action :create }.not_to raise_error expect { @resource.action :delete }.not_to raise_error expect { @resource.action :blues }.to raise_error(ArgumentError) end it "should use the object name as the path by default" do expect(@resource.path).to eql("fakey_fakerton") end it "should accept a string as the path" do expect { @resource.path "/tmp" }.not_to raise_error expect(@resource.path).to eql("/tmp") expect { @resource.path Hash.new }.to raise_error(ArgumentError) end it "should allow you to have specify whether the action is recursive with true/false" do expect { @resource.recursive true }.not_to raise_error expect { @resource.recursive false }.not_to raise_error expect { @resource.recursive "monkey" }.to raise_error(ArgumentError) end describe "when it has group, mode, and owner" do before do @resource.path("/tmp/foo/bar/") @resource.group("wheel") @resource.mode("0664") @resource.owner("root") end it "describes its state" do state = @resource.state expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0664") expect(state[:owner]).to eq("root") end it "returns the directory path as its identity" do expect(@resource.identity).to eq("/tmp/foo/bar/") end end end chef-12.3.0/spec/unit/resource/openbsd_package_spec.rb0000644000004100000410000000304712520074675022752 0ustar www-datawww-data# # Authors:: AJ Christensen () # Richard Manyanza () # Scott Bonds () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2014 Richard Manyanza, Scott Bonds # 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 'ostruct' describe Chef::Resource::OpenbsdPackage do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @resource = Chef::Resource::OpenbsdPackage.new("foo", @run_context) end describe "Initialization" do it "should return a Chef::Resource::OpenbsdPackage" do expect(@resource).to be_a_kind_of(Chef::Resource::OpenbsdPackage) end it "should set the resource_name to :openbsd_package" do expect(@resource.resource_name).to eql(:openbsd_package) end it "should not set the provider" do expect(@resource.provider).to be_nil end end end chef-12.3.0/spec/unit/resource/python_spec.rb0000644000004100000410000000230412520074675021161 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Python do before(:each) do @resource = Chef::Resource::Python.new("fakey_fakerton") end it "should create a new Chef::Resource::Python" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Python) end it "should have a resource name of :python" do expect(@resource.resource_name).to eql(:python) end it "should have an interpreter of python" do expect(@resource.interpreter).to eql("python") end end chef-12.3.0/spec/unit/resource/freebsd_package_spec.rb0000644000004100000410000000617112520074675022733 0ustar www-datawww-data# # Authors:: AJ Christensen () # Richard Manyanza () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2014 Richard Manyanza. # 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 'ostruct' describe Chef::Resource::FreebsdPackage do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @resource = Chef::Resource::FreebsdPackage.new("foo", @run_context) end describe "Initialization" do it "should return a Chef::Resource::FreebsdPackage" do expect(@resource).to be_a_kind_of(Chef::Resource::FreebsdPackage) end it "should set the resource_name to :freebsd_package" do expect(@resource.resource_name).to eql(:freebsd_package) end it "should not set the provider" do expect(@resource.provider).to be_nil end end describe "Assigning provider after creation" do describe "if ports specified as source" do it "should be Freebsd::Port" do @resource.source('ports') @resource.after_created expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Port) end end describe "if __Freebsd_version is greater than or equal to 1000017" do it "should be Freebsd::Pkgng" do [1000017, 1000018, 1000500, 1001001, 1100000].each do |__freebsd_version| @node.automatic_attrs[:os_version] = __freebsd_version @resource.after_created expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng) end end end describe "if pkgng enabled" do it "should be Freebsd::Pkgng" do pkg_enabled = OpenStruct.new(:stdout => "yes\n") allow(@resource).to receive(:shell_out!).with("make -V WITH_PKGNG", :env => nil).and_return(pkg_enabled) @resource.after_created expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng) end end describe "if __Freebsd_version is less than 1000017 and pkgng not enabled" do it "should be Freebsd::Pkg" do pkg_enabled = OpenStruct.new(:stdout => "\n") allow(@resource).to receive(:shell_out!).with("make -V WITH_PKGNG", :env => nil).and_return(pkg_enabled) [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version| @node.automatic_attrs[:os_version] = __freebsd_version @resource.after_created expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkg) end end end end end chef-12.3.0/spec/unit/resource/remote_directory_spec.rb0000644000004100000410000000576112520074675023231 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::RemoteDirectory do before(:each) do @resource = Chef::Resource::RemoteDirectory.new("/etc/dunk") end it "should create a new Chef::Resource::RemoteDirectory" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::RemoteDirectory) end it "should set the path to the first argument to new" do expect(@resource.path).to eql("/etc/dunk") end it "should accept a string for the remote directory source" do @resource.source "foo" expect(@resource.source).to eql("foo") end it "should have the basename of the remote directory resource as the default source" do expect(@resource.source).to eql("dunk") end it "should accept a number for the remote files backup" do @resource.files_backup 1 expect(@resource.files_backup).to eql(1) end it "should accept false for the remote files backup" do @resource.files_backup false expect(@resource.files_backup).to eql(false) end it "should accept 3 or 4 digets for the files_mode" do @resource.files_mode 100 expect(@resource.files_mode).to eql(100) @resource.files_mode 1000 expect(@resource.files_mode).to eql(1000) end it "should accept a string or number for the files group" do @resource.files_group "heart" expect(@resource.files_group).to eql("heart") @resource.files_group 1000 expect(@resource.files_group).to eql(1000) end it "should accept a string or number for the files owner" do @resource.files_owner "heart" expect(@resource.files_owner).to eql("heart") @resource.files_owner 1000 expect(@resource.files_owner).to eql(1000) end describe "when it has cookbook, files owner, files mode, and source" do before do @resource.path("/var/path/") @resource.cookbook("pokemon.rb") @resource.files_owner("root") @resource.files_group("supergroup") @resource.files_mode("0664") @resource.source("/var/source/") end it "describes its state" do state = @resource.state expect(state[:files_owner]).to eq("root") expect(state[:files_group]).to eq("supergroup") expect(state[:files_mode]).to eq("0664") end it "returns the path as its identity" do expect(@resource.identity).to eq("/var/path/") end end end chef-12.3.0/spec/unit/resource/remote_file_spec.rb0000644000004100000410000001515712520074675022144 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::RemoteFile do before(:each) do @resource = Chef::Resource::RemoteFile.new("fakey_fakerton") end describe "initialize" do it "should create a new Chef::Resource::RemoteFile" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::File) expect(@resource).to be_a_kind_of(Chef::Resource::RemoteFile) end end it "says its provider is RemoteFile when the source is an absolute URI" do @resource.source("http://www.google.com/robots.txt") expect(@resource.provider).to eq(Chef::Provider::RemoteFile) expect(Chef::Platform.find_provider(:noplatform, 'noversion', @resource)).to eq(Chef::Provider::RemoteFile) end describe "source" do it "does not have a default value for 'source'" do expect(@resource.source).to eql([]) end it "should accept a URI for the remote file source" do @resource.source "http://opscode.com/" expect(@resource.source).to eql([ "http://opscode.com/" ]) end it "should accept a delayed evalutator (string) for the remote file source" do @resource.source Chef::DelayedEvaluator.new {"http://opscode.com/"} expect(@resource.source).to eql([ "http://opscode.com/" ]) end it "should accept an array of URIs for the remote file source" do @resource.source([ "http://opscode.com/", "http://puppetlabs.com/" ]) expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ]) end it "should accept a delated evaluator (array) for the remote file source" do @resource.source Chef::DelayedEvaluator.new { [ "http://opscode.com/", "http://puppetlabs.com/" ] } expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ]) end it "should accept an multiple URIs as arguments for the remote file source" do @resource.source("http://opscode.com/", "http://puppetlabs.com/") expect(@resource.source).to eql([ "http://opscode.com/", "http://puppetlabs.com/" ]) end it "should only accept a single argument if a delayed evalutor is used" do expect { @resource.source("http://opscode.com/", Chef::DelayedEvaluator.new {"http://opscode.com/"}) }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI) end it "should only accept a single array item if a delayed evalutor is used" do expect { @resource.source(["http://opscode.com/", Chef::DelayedEvaluator.new {"http://opscode.com/"}]) }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI) end it "does not accept a non-URI as the source" do expect { @resource.source("not-a-uri") }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI) end it "does not accept a non-URI as the source when read from a delayed evaluator" do expect { @resource.source(Chef::DelayedEvaluator.new {"not-a-uri"}) @resource.source }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI) end it "should raise an exception when source is an empty array" do expect { @resource.source([]) }.to raise_error(ArgumentError) end end describe "checksum" do it "should accept a string for the checksum object" do @resource.checksum "asdf" expect(@resource.checksum).to eql("asdf") end it "should default to nil" do expect(@resource.checksum).to eq(nil) end end describe "ftp_active_mode" do it "should accept a boolean for the ftp_active_mode object" do @resource.ftp_active_mode true expect(@resource.ftp_active_mode).to be_truthy end it "should default to false" do expect(@resource.ftp_active_mode).to be_falsey end end describe "conditional get options" do it "defaults to using etags and last modified" do expect(@resource.use_etags).to be_truthy expect(@resource.use_last_modified).to be_truthy end it "enable or disables etag and last modified options as a group" do @resource.use_conditional_get(false) expect(@resource.use_etags).to be_falsey expect(@resource.use_last_modified).to be_falsey @resource.use_conditional_get(true) expect(@resource.use_etags).to be_truthy expect(@resource.use_last_modified).to be_truthy end it "disables etags indivdually" do @resource.use_etags(false) expect(@resource.use_etags).to be_falsey expect(@resource.use_last_modified).to be_truthy end it "disables last modified individually" do @resource.use_last_modified(false) expect(@resource.use_last_modified).to be_falsey expect(@resource.use_etags).to be_truthy end end describe "when it has group, mode, owner, source, and checksum" do before do if Chef::Platform.windows? @resource.path("C:/temp/origin/file.txt") @resource.rights(:read, "Everyone") @resource.deny_rights(:full_control, "Clumsy_Sam") else @resource.path("/this/path/") @resource.group("pokemon") @resource.mode("0664") @resource.owner("root") end @resource.source("https://www.google.com/images/srpr/logo3w.png") @resource.checksum("1"*26) end it "describes its state" do state = @resource.state if Chef::Platform.windows? puts state expect(state[:rights]).to eq([{:permissions => :read, :principals => "Everyone"}]) expect(state[:deny_rights]).to eq([{:permissions => :full_control, :principals => "Clumsy_Sam"}]) else expect(state[:group]).to eq("pokemon") expect(state[:mode]).to eq("0664") expect(state[:owner]).to eq("root") expect(state[:checksum]).to eq("1"*26) end end it "returns the path as its identity" do if Chef::Platform.windows? expect(@resource.identity).to eq("C:/temp/origin/file.txt") else expect(@resource.identity).to eq("/this/path/") end end end end chef-12.3.0/spec/unit/resource/solaris_package_spec.rb0000644000004100000410000000250412520074675022771 0ustar www-datawww-data# # Author:: Prabhu Das () # 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/resource/static_provider_resolution' describe Chef::Resource::SolarisPackage, "initialize" do %w{solaris2 nexentacore}.each do |platform_family| static_provider_resolution( resource: Chef::Resource::SolarisPackage, provider: Chef::Provider::Package::Solaris, name: :solaris_package, action: :install, os: "solaris2", platform_family: platform_family, ) end before(:each) do @resource = Chef::Resource::SolarisPackage.new("foo") end it "should set the package_name to the name provided" do expect(@resource.package_name).to eql("foo") end end chef-12.3.0/spec/unit/resource/portage_package_spec.rb0000644000004100000410000000245412520074675022762 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) describe Chef::Resource::PortagePackage, "initialize" do before(:each) do @resource = Chef::Resource::PortagePackage.new("foo") end it "should return a Chef::Resource::PortagePackage" do expect(@resource).to be_a_kind_of(Chef::Resource::PortagePackage) end it "should set the resource_name to :portage_package" do expect(@resource.resource_name).to eql(:portage_package) end it "should set the provider to Chef::Provider::Package::Portage" do expect(@resource.provider).to eql(Chef::Provider::Package::Portage) end end chef-12.3.0/spec/unit/resource/breakpoint_spec.rb0000644000004100000410000000257612520074675022011 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Breakpoint do static_provider_resolution( resource: Chef::Resource::Breakpoint, provider: Chef::Provider::Breakpoint, name: :breakpoint, action: :break, ) before do @breakpoint = Chef::Resource::Breakpoint.new end it "allows the action :break" do expect(@breakpoint.allowed_actions).to include(:break) end it "defaults to the break action" do expect(@breakpoint.action).to eq("break") end it "names itself after the line number of the file where it's created" do expect(@breakpoint.name).to match(/breakpoint_spec\.rb\:[\d]{2}\:in \`new\'$/) end end chef-12.3.0/spec/unit/resource/log_spec.rb0000644000004100000410000000416712520074675020432 0ustar www-datawww-data# # Author:: Cary Penniman () # Author:: Tyler Cloke () # 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' describe Chef::Resource::Log do before(:each) do @log_str = "this is my string to log" @resource = Chef::Resource::Log.new(@log_str) end it "should create a new Chef::Resource::Log" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Log) end it "supports the :write actions" do expect(@resource.allowed_actions).to include(:write) end it "should have a name of log" do expect(@resource.resource_name).to eq(:log) end it "should allow you to set a log string" do expect(@resource.name).to eq(@log_str) end it "should set the message to the first argument to new" do expect(@resource.message).to eq(@log_str) end it "should accept a string for the log message" do @resource.message "this is different" expect(@resource.message).to eq("this is different") end it "should accept a vaild level option" do @resource.level :debug @resource.level :info @resource.level :warn @resource.level :error @resource.level :fatal expect { @resource.level :unsupported }.to raise_error(ArgumentError) end describe "when the identity is defined" do before do @resource = Chef::Resource::Log.new("ery day I'm loggin-in") end it "returns the log string as its identity" do expect(@resource.identity).to eq("ery day I'm loggin-in") end end end chef-12.3.0/spec/unit/resource/dsc_resource_spec.rb0000644000004100000410000000633312520074675022326 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::DscResource do let(:dsc_test_resource_name) { 'DSCTest' } let(:dsc_test_property_name) { :DSCTestProperty } let(:dsc_test_property_value) { 'DSCTestValue' } context 'when Powershell supports Dsc' do let(:dsc_test_run_context) { node = Chef::Node.new node.automatic[:languages][:powershell][:version] = '5.0.10018.0' empty_events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, empty_events) } let(:dsc_test_resource) { Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context) } it "has a default action of `:run`" do expect(dsc_test_resource.action).to eq(:run) end it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do expect(dsc_test_resource.allowed_actions.to_set).to eq([:run,:nothing].to_set) end it "allows the resource attribute to be set" do dsc_test_resource.resource(dsc_test_resource_name) expect(dsc_test_resource.resource).to eq(dsc_test_resource_name) end it "allows the module_name attribute to be set" do dsc_test_resource.module_name(dsc_test_resource_name) expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name) end context "when setting a dsc property" do it "allows setting a dsc property with a property name of type Symbol" do dsc_test_resource.property(dsc_test_property_name, dsc_test_property_value) expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value) expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value) end it "raises a TypeError if property_name is not a symbol" do expect{ dsc_test_resource.property('Foo', dsc_test_property_value) }.to raise_error(TypeError) end context "when using DelayedEvaluators" do it "allows setting a dsc property with a property name of type Symbol" do dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new { dsc_test_property_value }) expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value) expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value) end end end context 'Powershell DSL methods' do it "responds to :ps_credential" do expect(dsc_test_resource.respond_to?(:ps_credential)).to be true end end end end chef-12.3.0/spec/unit/resource/erl_call_spec.rb0000644000004100000410000000453112520074675021421 0ustar www-datawww-data# # Author:: Joe Williams () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Joe Williams # 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::ErlCall do before(:each) do @resource = Chef::Resource::ErlCall.new("fakey_fakerton") end it "should create a new Chef::Resource::ErlCall" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::ErlCall) end it "should have a resource name of :erl_call" do expect(@resource.resource_name).to eql(:erl_call) end it "should have a default action of run" do expect(@resource.action).to eql("run") end it "should accept run as an action" do expect { @resource.action :run }.not_to raise_error end it "should allow you to set the code attribute" do @resource.code "q()." expect(@resource.code).to eql("q().") end it "should allow you to set the cookie attribute" do @resource.cookie "nomnomnom" expect(@resource.cookie).to eql("nomnomnom") end it "should allow you to set the distributed attribute" do @resource.distributed true expect(@resource.distributed).to eql(true) end it "should allow you to set the name_type attribute" do @resource.name_type "sname" expect(@resource.name_type).to eql("sname") end it "should allow you to set the node_name attribute" do @resource.node_name "chef@erlang" expect(@resource.node_name).to eql("chef@erlang") end describe "when it has cookie and node_name" do before do @resource.code("erl-call:function()") @resource.cookie("cookie") @resource.node_name("raster") end it "returns the code as its identity" do expect(@resource.identity).to eq("erl-call:function()") end end end chef-12.3.0/spec/unit/resource/smartos_package_spec.rb0000644000004100000410000000206112520074675023003 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2010 Thomas Bishop # 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/resource/static_provider_resolution' describe Chef::Resource::SmartosPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::SmartosPackage, provider: Chef::Provider::Package::SmartOS, name: :smartos_package, action: :install, os: "solaris2", platform_family: "smartos", ) end chef-12.3.0/spec/unit/resource/file_spec.rb0000644000004100000410000001044712520074675020566 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::File do before(:each) do @resource = Chef::Resource::File.new("fakey_fakerton") end it "should have a name" do expect(@resource.name).to eql("fakey_fakerton") end it "should have a default action of 'create'" do expect(@resource.action).to eql("create") end it "should have a default content of nil" do expect(@resource.content).to be_nil end it "should be set to back up 5 files by default" do expect(@resource.backup).to eql(5) end it "should only accept strings for content" do expect { @resource.content 5 }.to raise_error(ArgumentError) expect { @resource.content :foo }.to raise_error(ArgumentError) expect { @resource.content "hello" => "there" }.to raise_error(ArgumentError) expect { @resource.content "hi" }.not_to raise_error end it "should only accept false or a number for backup" do expect { @resource.backup true }.to raise_error(ArgumentError) expect { @resource.backup false }.not_to raise_error expect { @resource.backup 10 }.not_to raise_error expect { @resource.backup "blues" }.to raise_error(ArgumentError) end it "should accept a sha256 for checksum" do expect { @resource.checksum "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa" }.not_to raise_error expect { @resource.checksum "monkey!" }.to raise_error(ArgumentError) end it "should accept create, delete or touch for action" do expect { @resource.action :create }.not_to raise_error expect { @resource.action :delete }.not_to raise_error expect { @resource.action :touch }.not_to raise_error expect { @resource.action :blues }.to raise_error(ArgumentError) end it "should accept a block, symbol, or string for verify" do expect {@resource.verify {}}.not_to raise_error expect {@resource.verify ""}.not_to raise_error expect {@resource.verify :json}.not_to raise_error expect {@resource.verify true}.to raise_error expect {@resource.verify false}.to raise_error end it "should accept multiple verify statements" do @resource.verify "foo" @resource.verify "bar" @resource.verify.length == 2 end it "should use the object name as the path by default" do expect(@resource.path).to eql("fakey_fakerton") end it "should accept a string as the path" do expect { @resource.path "/tmp" }.not_to raise_error expect(@resource.path).to eql("/tmp") expect { @resource.path Hash.new }.to raise_error(ArgumentError) end describe "when it has a path, owner, group, mode, and checksum" do before do @resource.path("/tmp/foo.txt") @resource.owner("root") @resource.group("wheel") @resource.mode("0644") @resource.checksum("1" * 64) end context "on unix", :unix_only do it "describes its state" do state = @resource.state expect(state[:owner]).to eq("root") expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0644") expect(state[:checksum]).to eq("1" * 64) end end it "returns the file path as its identity" do expect(@resource.identity).to eq("/tmp/foo.txt") end end describe "when access controls are set on windows", :windows_only => true do before do @resource.rights :read, "Everyone" @resource.rights :full_control, "DOMAIN\User" end it "describes its state including windows ACL attributes" do state = @resource.state expect(state[:rights]).to eq([ {:permissions => :read, :principals => "Everyone"}, {:permissions => :full_control, :principals => "DOMAIN\User"} ]) end end end chef-12.3.0/spec/unit/resource/dpkg_package_spec.rb0000644000004100000410000000177012520074675022246 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::DpkgPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::DpkgPackage, provider: Chef::Provider::Package::Dpkg, name: :dpkg_package, action: :install, os: 'linux', ) end chef-12.3.0/spec/unit/resource/timestamped_deploy_spec.rb0000644000004100000410000000174112520074675023534 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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::TimestampedDeploy, "initialize" do static_provider_resolution( resource: Chef::Resource::TimestampedDeploy, provider: Chef::Provider::Deploy::Timestamped, name: :deploy, action: :deploy, os: 'linux', platform_family: 'rhel', ) end chef-12.3.0/spec/unit/resource/mdadm_spec.rb0000644000004100000410000000572012520074675020727 0ustar www-datawww-data# # Author:: Joe Williams () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Joe Williams # 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::Mdadm do before(:each) do @resource = Chef::Resource::Mdadm.new("fakey_fakerton") end it "should create a new Chef::Resource::Mdadm" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Mdadm) end it "should have a resource name of :mdadm" do expect(@resource.resource_name).to eql(:mdadm) end it "should have a default action of create" do expect(@resource.action).to eql(:create) end it "should accept create, assemble, stop as actions" do expect { @resource.action :create }.not_to raise_error expect { @resource.action :assemble }.not_to raise_error expect { @resource.action :stop }.not_to raise_error end it "should allow you to set the raid_device attribute" do @resource.raid_device "/dev/md3" expect(@resource.raid_device).to eql("/dev/md3") end it "should allow you to set the chunk attribute" do @resource.chunk 256 expect(@resource.chunk).to eql(256) end it "should allow you to set the level attribute" do @resource.level 1 expect(@resource.level).to eql(1) end it "should allow you to set the metadata attribute" do @resource.metadata "1.2" expect(@resource.metadata).to eql("1.2") end it "should allow you to set the bitmap attribute" do @resource.metadata "internal" expect(@resource.metadata).to eql("internal") end it "should allow you to set the devices attribute" do @resource.devices ["/dev/sda", "/dev/sdb"] expect(@resource.devices).to eql(["/dev/sda", "/dev/sdb"]) end it "should allow you to set the exists attribute" do @resource.exists true expect(@resource.exists).to eql(true) end describe "when it has devices, level, and chunk" do before do @resource.raid_device("raider") @resource.devices(["device1", "device2"]) @resource.level(1) @resource.chunk(42) end it "describes its state" do state = @resource.state expect(state[:devices]).to eql(["device1", "device2"]) expect(state[:level]).to eq(1) expect(state[:chunk]).to eq(42) end it "returns the raid device as its identity" do expect(@resource.identity).to eq("raider") end end end chef-12.3.0/spec/unit/resource/easy_install_package_spec.rb0000644000004100000410000000246612520074675024013 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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/resource/static_provider_resolution' describe Chef::Resource::EasyInstallPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::EasyInstallPackage, provider: Chef::Provider::Package::EasyInstall, name: :easy_install_package, action: :install, ) before(:each) do @resource = Chef::Resource::EasyInstallPackage.new("foo") end it "should allow you to set the easy_install_binary attribute" do @resource.easy_install_binary "/opt/local/bin/easy_install" expect(@resource.easy_install_binary).to eql("/opt/local/bin/easy_install") end end chef-12.3.0/spec/unit/resource/deploy_spec.rb0000644000004100000410000002417112520074675021142 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::Deploy do static_provider_resolution( resource: Chef::Resource::Deploy, provider: Chef::Provider::Deploy::Timestamped, name: :deploy, action: :deploy, ) class << self def resource_has_a_string_attribute(attr_name) it "has a String attribute for #{attr_name.to_s}" do @resource.send(attr_name, "this is a string") expect(@resource.send(attr_name)).to eql("this is a string") expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError) end end def resource_has_a_boolean_attribute(attr_name, opts={:defaults_to=>false}) it "has a Boolean attribute for #{attr_name.to_s}" do expect(@resource.send(attr_name)).to eql(opts[:defaults_to]) @resource.send(attr_name, !opts[:defaults_to]) expect(@resource.send(attr_name)).to eql( !opts[:defaults_to] ) end end def resource_has_a_callback_attribute(attr_name) it "has a Callback attribute #{attr_name}" do callback_block = lambda { :noop } expect {@resource.send(attr_name, &callback_block)}.not_to raise_error expect(@resource.send(attr_name)).to eq(callback_block) callback_file = "path/to/callback.rb" expect {@resource.send(attr_name, callback_file)}.not_to raise_error expect(@resource.send(attr_name)).to eq(callback_file) expect {@resource.send(attr_name, :this_is_fail)}.to raise_error(ArgumentError) end end end before do @resource = Chef::Resource::Deploy.new("/my/deploy/dir") end resource_has_a_string_attribute(:repo) resource_has_a_string_attribute(:deploy_to) resource_has_a_string_attribute(:role) resource_has_a_string_attribute(:restart_command) resource_has_a_string_attribute(:migration_command) resource_has_a_string_attribute(:user) resource_has_a_string_attribute(:group) resource_has_a_string_attribute(:repository_cache) resource_has_a_string_attribute(:copy_exclude) resource_has_a_string_attribute(:revision) resource_has_a_string_attribute(:remote) resource_has_a_string_attribute(:git_ssh_wrapper) resource_has_a_string_attribute(:svn_username) resource_has_a_string_attribute(:svn_password) resource_has_a_string_attribute(:svn_arguments) resource_has_a_string_attribute(:svn_info_args) resource_has_a_boolean_attribute(:migrate, :defaults_to=>false) resource_has_a_boolean_attribute(:enable_submodules, :defaults_to=>false) resource_has_a_boolean_attribute(:shallow_clone, :defaults_to=>false) it "uses the first argument as the deploy directory" do expect(@resource.deploy_to).to eql("/my/deploy/dir") end # For git, any revision, branch, tag, whatever is resolved to a SHA1 ref. # For svn, the branch is included in the repo URL. # Therefore, revision and branch ARE NOT SEPARATE THINGS it "aliases #revision as #branch" do @resource.branch "stable" expect(@resource.revision).to eql("stable") end it "takes the SCM resource to use as a constant, and defaults to git" do expect(@resource.scm_provider).to eql(Chef::Provider::Git) @resource.scm_provider Chef::Provider::Subversion expect(@resource.scm_provider).to eql(Chef::Provider::Subversion) end it "allows scm providers to be set via symbol" do expect(@resource.scm_provider).to eq(Chef::Provider::Git) @resource.scm_provider :subversion expect(@resource.scm_provider).to eq(Chef::Provider::Subversion) end it "allows scm providers to be set via string" do expect(@resource.scm_provider).to eq(Chef::Provider::Git) @resource.scm_provider "subversion" expect(@resource.scm_provider).to eq(Chef::Provider::Subversion) end it "has a boolean attribute for svn_force_export defaulting to false" do expect(@resource.svn_force_export).to be_falsey @resource.svn_force_export true expect(@resource.svn_force_export).to be_truthy expect {@resource.svn_force_export(10053)}.to raise_error(ArgumentError) end it "takes arbitrary environment variables in a hash" do @resource.environment "RAILS_ENV" => "production" expect(@resource.environment).to eq({"RAILS_ENV" => "production"}) end it "takes string arguments to environment for backwards compat, setting RAILS_ENV, RACK_ENV, and MERB_ENV" do @resource.environment "production" expect(@resource.environment).to eq({"RAILS_ENV"=>"production", "RACK_ENV"=>"production","MERB_ENV"=>"production"}) end it "sets destination to $deploy_to/shared/$repository_cache" do expect(@resource.destination).to eql("/my/deploy/dir/shared/cached-copy") end it "sets shared_path to $deploy_to/shared" do expect(@resource.shared_path).to eql("/my/deploy/dir/shared") end it "sets current_path to $deploy_to/current" do expect(@resource.current_path).to eql("/my/deploy/dir/current") end it "gets the current_path correct even if the shared_path is set (regression test)" do @resource.shared_path expect(@resource.current_path).to eql("/my/deploy/dir/current") end it "gives #depth as 5 if shallow clone is true, nil otherwise" do expect(@resource.depth).to be_nil @resource.shallow_clone true expect(@resource.depth).to eql("5") end it "aliases repo as repository" do @resource.repository "git@github.com/opcode/cookbooks.git" expect(@resource.repo).to eql("git@github.com/opcode/cookbooks.git") end it "aliases git_ssh_wrapper as ssh_wrapper" do @resource.ssh_wrapper "git_my_repo.sh" expect(@resource.git_ssh_wrapper).to eql("git_my_repo.sh") end it "has an Array attribute purge_before_symlink, default: log, tmp/pids, public/system" do expect(@resource.purge_before_symlink).to eq(%w{ log tmp/pids public/system }) @resource.purge_before_symlink %w{foo bar baz} expect(@resource.purge_before_symlink).to eq(%w{foo bar baz}) end it "has an Array attribute create_dirs_before_symlink, default: tmp, public, config" do expect(@resource.create_dirs_before_symlink).to eq(%w{tmp public config}) @resource.create_dirs_before_symlink %w{foo bar baz} expect(@resource.create_dirs_before_symlink).to eq(%w{foo bar baz}) end it 'has a Hash attribute symlinks, default: {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}' do default = { "system" => "public/system", "pids" => "tmp/pids", "log" => "log"} expect(@resource.symlinks).to eq(default) @resource.symlinks "foo" => "bar/baz" expect(@resource.symlinks).to eq({"foo" => "bar/baz"}) end it 'has a Hash attribute symlink_before_migrate, default "config/database.yml" => "config/database.yml"' do expect(@resource.symlink_before_migrate).to eq({"config/database.yml" => "config/database.yml"}) @resource.symlink_before_migrate "wtf?" => "wtf is going on" expect(@resource.symlink_before_migrate).to eq({"wtf?" => "wtf is going on"}) end resource_has_a_callback_attribute :before_migrate resource_has_a_callback_attribute :before_symlink resource_has_a_callback_attribute :before_restart resource_has_a_callback_attribute :after_restart it "aliases restart_command as restart" do @resource.restart "foobaz" expect(@resource.restart_command).to eq("foobaz") end it "takes a block for the restart parameter" do restart_like_this = lambda {p :noop} @resource.restart(&restart_like_this) expect(@resource.restart).to eq(restart_like_this) end it "allows providers to be set with a full class name" do @resource.provider Chef::Provider::Deploy::Timestamped expect(@resource.provider).to eq(Chef::Provider::Deploy::Timestamped) end it "allows deploy providers to be set via symbol" do @resource.provider :revision expect(@resource.provider).to eq(Chef::Provider::Deploy::Revision) end it "allows deploy providers to be set via string" do @resource.provider "revision" expect(@resource.provider).to eq(Chef::Provider::Deploy::Revision) end it "defaults keep_releases to 5" do expect(@resource.keep_releases).to eq(5) end it "allows keep_releases to be set via integer" do @resource.keep_releases 10 expect(@resource.keep_releases).to eq(10) end it "enforces a minimum keep_releases of 1" do @resource.keep_releases 0 expect(@resource.keep_releases).to eq(1) end describe "when it has a timeout attribute" do let(:ten_seconds) { 10 } before { @resource.timeout(ten_seconds) } it "stores this timeout" do expect(@resource.timeout).to eq(ten_seconds) end end describe "when it has no timeout attribute" do it "should have no default timeout" do expect(@resource.timeout).to be_nil end end describe "when it has meta application root, revision, user, group, scm provider, repository cache, environment, simlinks and migrate" do before do @resource.repository("http://uri.org") @resource.deploy_to("/") @resource.revision("1.2.3") @resource.user("root") @resource.group("pokemon") @resource.scm_provider(Chef::Provider::Git) @resource.repository_cache("cached-copy") @resource.environment({"SUDO" => "TRUE"}) @resource.symlinks({"system" => "public/system"}) @resource.migrate(false) end it "describes its state" do state = @resource.state expect(state[:deploy_to]).to eq("/") expect(state[:revision]).to eq("1.2.3") end it "returns the repository URI as its identity" do expect(@resource.identity).to eq("http://uri.org") end end end chef-12.3.0/spec/unit/resource/registry_key_spec.rb0000644000004100000410000001527712520074675022375 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::Resource::RegistryKey, "initialize" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should create a new Chef::Resource::RegistryKey" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::RegistryKey) end it "should set the resource_name to :registry_key" do expect(@resource.resource_name).to eql(:registry_key) end it "should set the key equal to the argument to initialize" do expect(@resource.key).to eql('HKCU\Software\Raxicoricofallapatorius') end it "should default recursive to false" do expect(@resource.recursive).to eql(false) end it "should default architecture to :machine" do expect(@resource.architecture).to eql(:machine) end it "should set action to :create" do expect(@resource.action).to eql(:create) end %w{create create_if_missing delete delete_key}.each do |action| it "should allow action #{action}" do expect(@resource.allowed_actions.detect { |a| a == action.to_sym }).to eql(action.to_sym) end end end describe Chef::Resource::RegistryKey, "key" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should allow a string" do @resource.key 'HKCU\Software\Poosh' expect(@resource.key).to eql('HKCU\Software\Poosh') end it "should not allow an integer" do expect { @resource.send(:key, 100) }.to raise_error(ArgumentError) end it "should not allow a hash" do expect { @resource.send(:key, { :sonic => "screwdriver" }) }.to raise_error(ArgumentError) end end describe Chef::Resource::RegistryKey, "values" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should allow a single proper hash of registry values" do @resource.values( { :name => 'poosh', :type => :string, :data => 'carmen' } ) expect(@resource.values).to eql([ { :name => 'poosh', :type => :string, :data => 'carmen' } ]) end it "should allow an array of proper hashes of registry values" do @resource.values [ { :name => 'poosh', :type => :string, :data => 'carmen' } ] expect(@resource.values).to eql([ { :name => 'poosh', :type => :string, :data => 'carmen' } ]) end it "should return checksummed data if the type is unsafe" do @resource.values( { :name => 'poosh', :type => :binary, :data => 255.chr * 1 }) expect(@resource.values).to eql([ { :name => 'poosh', :type => :binary, :data => "00594fd4f42ba43fc1ca0427a0576295" } ]) end it "should throw an exception if the name field is missing" do expect { @resource.values [ { :type => :string, :data => 'carmen' } ] }.to raise_error(ArgumentError) end it "should throw an exception if the type field is missing" do expect { @resource.values [ { :name => 'poosh', :data => 'carmen' } ] }.to raise_error(ArgumentError) end it "should throw an exception if the data field is missing" do expect { @resource.values [ { :name => 'poosh', :type => :string } ] }.to raise_error(ArgumentError) end it "should throw an exception if extra fields are present" do expect { @resource.values [ { :name => 'poosh', :type => :string, :data => 'carmen', :screwdriver => 'sonic' } ] }.to raise_error(ArgumentError) end it "should not allow a string" do expect { @resource.send(:values, 'souffle') }.to raise_error(ArgumentError) end it "should not allow an integer" do expect { @resource.send(:values, 100) }.to raise_error(ArgumentError) end end describe Chef::Resource::RegistryKey, "recursive" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should allow a boolean" do @resource.recursive(true) expect(@resource.recursive).to eql(true) end it "should not allow a hash" do expect { @resource.recursive({:sonic => :screwdriver}) }.to raise_error(ArgumentError) end it "should not allow an array" do expect { @resource.recursive([:nose, :chin]) }.to raise_error(ArgumentError) end it "should not allow a string" do expect { @resource.recursive('souffle') }.to raise_error(ArgumentError) end it "should not allow an integer" do expect { @resource.recursive(100) }.to raise_error(ArgumentError) end end describe Chef::Resource::RegistryKey, "architecture" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end [ :i386, :x86_64, :machine ].each do |arch| it "should allow #{arch} as a symbol" do @resource.architecture(arch) expect(@resource.architecture).to eql(arch) end end it "should not allow a hash" do expect { @resource.architecture({:sonic => :screwdriver}) }.to raise_error(ArgumentError) end it "should not allow an array" do expect { @resource.architecture([:nose, :chin]) }.to raise_error(ArgumentError) end it "should not allow a string" do expect { @resource.architecture('souffle') }.to raise_error(ArgumentError) end it "should not allow an integer" do expect { @resource.architecture(100) }.to raise_error(ArgumentError) end end describe Chef::Resource::RegistryKey, ":unscrubbed_values" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should return unsafe data as-is" do key_values = [ { :name => 'poosh', :type => :binary, :data => 255.chr * 1 } ] @resource.values(key_values) expect(@resource.unscrubbed_values).to eql(key_values) end end describe Chef::Resource::RegistryKey, "state" do before(:each) do @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius') end it "should return scrubbed values" do @resource.values([ { :name => 'poosh', :type => :binary, :data => 255.chr * 1 } ]) expect(@resource.state).to eql( { :values => [{ :name => 'poosh', :type => :binary, :data => "00594fd4f42ba43fc1ca0427a0576295" }] } ) end end chef-12.3.0/spec/unit/resource/homebrew_package_spec.rb0000644000004100000410000000301112520074675023117 0ustar www-datawww-data# # Author:: Joshua Timberman () # Copyright (c) 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::HomebrewPackage, 'initialize' do static_provider_resolution( resource: Chef::Resource::HomebrewPackage, provider: Chef::Provider::Package::Homebrew, name: :homebrew_package, action: :install, os: "mac_os_x", ) let(:resource) { Chef::Resource::HomebrewPackage.new('emacs') } shared_examples 'home_brew user set and returned' do it 'returns the configured homebrew_user' do resource.homebrew_user user expect(resource.homebrew_user).to eql(user) end end context 'homebrew_user is set' do let(:user) { 'Captain Picard' } include_examples 'home_brew user set and returned' context 'as an integer' do let(:user) { 1001 } include_examples 'home_brew user set and returned' end end end chef-12.3.0/spec/unit/resource/template_spec.rb0000644000004100000410000001505312520074675021460 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Template do before(:each) do @resource = Chef::Resource::Template.new("fakey_fakerton") end describe "initialize" do it "should create a new Chef::Resource::Template" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::File) expect(@resource).to be_a_kind_of(Chef::Resource::Template) end end describe "source" do it "should accept a string for the template source" do @resource.source "something" expect(@resource.source).to eql("something") end it "should have a default based on the param name with .erb appended" do expect(@resource.source).to eql("fakey_fakerton.erb") end it "should use only the basename of the file as the default" do r = Chef::Resource::Template.new("/tmp/obit/fakey_fakerton") expect(r.source).to eql("fakey_fakerton.erb") end end describe "variables" do it "should accept a hash for the variable list" do @resource.variables({ :reluctance => :awkward }) expect(@resource.variables).to eq({ :reluctance => :awkward }) end end describe "cookbook" do it "should accept a string for the cookbook name" do @resource.cookbook("foo") expect(@resource.cookbook).to eq("foo") end it "should default to nil" do expect(@resource.cookbook).to eq(nil) end end describe "local" do it "should accept a boolean for whether a template is local or remote" do @resource.local(true) expect(@resource.local).to eq(true) end it "should default to false" do expect(@resource.local).to eq(false) end end describe "when it has a path, owner, group, mode, and checksum" do before do @resource.path("/tmp/foo.txt") @resource.owner("root") @resource.group("wheel") @resource.mode("0644") @resource.checksum("1" * 64) end context "on unix", :unix_only do it "describes its state" do state = @resource.state expect(state[:owner]).to eq("root") expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0644") expect(state[:checksum]).to eq("1" * 64) end end context "on windows", :windows_only do # according to Chef::Resource::File, windows state attributes are rights + deny_rights pending "it describes its state" end it "returns the file path as its identity" do expect(@resource.identity).to eq("/tmp/foo.txt") end end describe "defining helper methods" do module ExampleHelpers def static_example "static_example" end end it "collects helper method bodies as blocks" do @resource.helper(:example_1) { "example_1" } @resource.helper(:example_2) { "example_2" } expect(@resource.inline_helper_blocks[:example_1].call).to eq("example_1") expect(@resource.inline_helper_blocks[:example_2].call).to eq("example_2") end it "compiles helper methods into a module" do @resource.helper(:example_1) { "example_1" } @resource.helper(:example_2) { "example_2" } modules = @resource.helper_modules expect(modules.size).to eq(1) o = Object.new modules.each {|m| o.extend(m)} expect(o.example_1).to eq("example_1") expect(o.example_2).to eq("example_2") end it "compiles helper methods with arguments into a module" do @resource.helper(:shout) { |quiet| quiet.upcase } modules = @resource.helper_modules o = Object.new modules.each {|m| o.extend(m)} expect(o.shout("shout")).to eq("SHOUT") end it "raises an error when attempting to define a helper method without a method body" do expect { @resource.helper(:example) }.to raise_error(Chef::Exceptions::ValidationFailed) end it "raises an error when attempting to define a helper method with a non-Symbod method name" do expect { @resource.helper("example") { "fail" } }.to raise_error(Chef::Exceptions::ValidationFailed) end it "collects helper module bodies as blocks" do @resource.helpers do def example_1 "example_1" end end module_body = @resource.inline_helper_modules.first expect(module_body).to be_a(Proc) end it "compiles helper module bodies into modules" do @resource.helpers do def example_1 "example_1" end end modules = @resource.helper_modules expect(modules.size).to eq(1) o = Object.new modules.each {|m| o.extend(m)} expect(o.example_1).to eq("example_1") end it "raises an error when no block or module name is given for helpers definition" do expect { @resource.helpers() }.to raise_error(Chef::Exceptions::ValidationFailed) end it "raises an error when a non-module is given for helpers definition" do expect { @resource.helpers("NotAModule") }.to raise_error(Chef::Exceptions::ValidationFailed) end it "raises an error when a module name and block are both given for helpers definition" do expect { @resource.helpers(ExampleHelpers) { module_code } }.to raise_error(Chef::Exceptions::ValidationFailed) end it "collects helper modules" do @resource.helpers(ExampleHelpers) expect(@resource.helper_modules).to include(ExampleHelpers) end it "combines all helpers into a set of compiled modules" do @resource.helpers(ExampleHelpers) @resource.helpers do def inline_module "inline_module" end end @resource.helper(:inline_method) { "inline_method" } expect(@resource.helper_modules.size).to eq(3) o = Object.new @resource.helper_modules.each {|m| o.extend(m)} expect(o.static_example).to eq("static_example") expect(o.inline_module).to eq("inline_module") expect(o.inline_method).to eq("inline_method") end end end chef-12.3.0/spec/unit/resource/http_request_spec.rb0000644000004100000410000000340712520074675022374 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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' describe Chef::Resource::HttpRequest do before(:each) do @resource = Chef::Resource::HttpRequest.new("fakey_fakerton") end it "should create a new Chef::Resource::HttpRequest" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::HttpRequest) end it "should set url to a string" do @resource.url "http://slashdot.org" expect(@resource.url).to eql("http://slashdot.org") end it "should set the message to the name by default" do expect(@resource.message).to eql("fakey_fakerton") end it "should set message to a string" do @resource.message "monkeybars" expect(@resource.message).to eql("monkeybars") end describe "when it has a message and headers" do before do @resource.url("http://www.trololol.net") @resource.message("Get sum post brah.") @resource.headers({"head" => "tail"}) end it "returns the url as its identity" do expect(@resource.identity).to eq("http://www.trololol.net") end end end chef-12.3.0/spec/unit/resource/csh_spec.rb0000644000004100000410000000225412520074675020421 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Csh do before(:each) do @resource = Chef::Resource::Csh.new("fakey_fakerton") end it "should create a new Chef::Resource::Csh" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Csh) end it "should have a resource name of :csh" do expect(@resource.resource_name).to eql(:csh) end it "should have an interpreter of csh" do expect(@resource.interpreter).to eql("csh") end end chef-12.3.0/spec/unit/resource/ohai_spec.rb0000644000004100000410000000325212520074675020563 0ustar www-datawww-data# # Author:: Michael Leinartas () # Copyright:: Copyright (c) 2010 Michael Leinartas # 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::Ohai do before(:each) do @resource = Chef::Resource::Ohai.new("ohai_reload") end it "should create a new Chef::Resource::Ohai" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Ohai) end it "should have a resource name of :ohai" do expect(@resource.resource_name).to eql(:ohai) end it "should have a default action of create" do expect(@resource.action).to eql(:reload) end it "should allow you to set the plugin attribute" do @resource.plugin "passwd" expect(@resource.plugin).to eql("passwd") end describe "when it has a plugin value" do before do @resource.name("test") @resource.plugin("passwd") end it "describes its state" do state = @resource.state expect(state[:plugin]).to eq("passwd") end it "returns the name as its identity" do expect(@resource.identity).to eq("test") end end end chef-12.3.0/spec/unit/resource/perl_spec.rb0000644000004100000410000000226412520074675020607 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Resource::Perl do before(:each) do @resource = Chef::Resource::Perl.new("fakey_fakerton") end it "should create a new Chef::Resource::Perl" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Perl) end it "should have a resource name of :perl" do expect(@resource.resource_name).to eql(:perl) end it "should have an interpreter of perl" do expect(@resource.interpreter).to eql("perl") end end chef-12.3.0/spec/unit/resource/powershell_spec.rb0000644000004100000410000001401712520074675022030 0ustar www-datawww-data# # Author:: Adam Edwards () # 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::PowershellScript 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::PowershellScript.new("powershell_unit_test", run_context) end it "should create a new Chef::Resource::PowershellScript" do expect(@resource).to be_a_kind_of(Chef::Resource::PowershellScript) end it "should set convert_boolean_return to false by default" do expect(@resource.convert_boolean_return).to eq(false) end it "should return the value for convert_boolean_return that was set" do @resource.convert_boolean_return true expect(@resource.convert_boolean_return).to eq(true) @resource.convert_boolean_return false expect(@resource.convert_boolean_return).to eq(false) end context "when using guards" do let(:resource) { @resource } before(:each) do allow(resource).to receive(:run_action) allow(resource).to receive(:updated).and_return(true) end it "inherits exactly the :cwd, :environment, :group, :path, :user, :umask, and :architecture attributes from a parent resource class" do inherited_difference = Chef::Resource::PowershellScript.guard_inherited_attributes - [:cwd, :environment, :group, :path, :user, :umask, :architecture ] expect(inherited_difference).to eq([]) end it "should allow guard interpreter to be set to Chef::Resource::Script" do resource.guard_interpreter(:script) allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false) resource.only_if("echo hi") end it "should allow guard interpreter to be set to Chef::Resource::Bash derived from Chef::Resource::Script" do resource.guard_interpreter(:bash) allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false) resource.only_if("echo hi") end it "should allow guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do resource.guard_interpreter(:powershell_script) allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false) resource.only_if("echo hi") end it "should enable convert_boolean_return by default for guards in the context of powershell_script when no guard params are specified" do allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true) allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with( {:convert_boolean_return => true, :code => "$true"}).and_return(Proc.new {}) resource.only_if("$true") end it "should enable convert_boolean_return by default for guards in non-Chef::Resource::Script derived resources when no guard params are specified" do node = Chef::Node.new run_context = Chef::RunContext.new(node, nil, nil) file_resource = Chef::Resource::File.new('idontexist', run_context) file_resource.guard_interpreter :powershell_script allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with( {:convert_boolean_return => true, :code => "$true"}).and_return(Proc.new {}) resource.only_if("$true") end it "should enable convert_boolean_return by default for guards in the context of powershell_script when guard params are specified" do guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64} allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with( {:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {}) resource.only_if("$true", guard_parameters) end it "should pass convert_boolean_return as true if it was specified as true in a guard parameter" do guard_parameters = {:cwd => '/etc/chef', :convert_boolean_return => true, :architecture => :x86_64} allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with( {:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {}) resource.only_if("$true", guard_parameters) end it "should pass convert_boolean_return as false if it was specified as true in a guard parameter" do other_guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64} parameters_with_boolean_disabled = other_guard_parameters.merge({:convert_boolean_return => false, :code => "$true"}) allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with( parameters_with_boolean_disabled).and_return(Proc.new {}) resource.only_if("$true", parameters_with_boolean_disabled) end end context "as a script running in Windows-based scripting language" do let(:resource_instance) { @resource } let(:resource_instance_name ) { @resource.command } let(:resource_name) { :powershell_script } let(:interpreter_file_name) { 'powershell.exe' } it_should_behave_like "a Windows script resource" end end chef-12.3.0/spec/unit/resource/conditional_spec.rb0000644000004100000410000001646012520074675022153 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'spec_helper' require 'ostruct' describe Chef::Resource::Conditional do before do allow_any_instance_of(Mixlib::ShellOut).to receive(:run_command).and_return(nil) @status = OpenStruct.new(:success? => true) allow_any_instance_of(Mixlib::ShellOut).to receive(:status).and_return(@status) @parent_resource = Chef::Resource.new(nil, Chef::Node.new) end it "raises an exception when neither a block or command is given" do expect { Chef::Resource::Conditional.send(:new, :always, @parent_resource, nil, {})}.to raise_error(ArgumentError, /requires either a command or a block/) end it "does not evaluate a guard interpreter on initialization of the conditional" do expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:configure) expect(Chef::GuardInterpreter::DefaultGuardInterpreter).not_to receive(:new) expect(Chef::GuardInterpreter::ResourceGuardInterpreter).not_to receive(:new) Chef::Resource::Conditional.only_if(@parent_resource, "true") end describe "configure" do it "raises an exception when a guard_interpreter is specified and a block is given" do @parent_resource.guard_interpreter :canadian_mounties conditional = Chef::Resource::Conditional.send(:new, :always, @parent_resource, nil, {}) { True } expect { conditional.configure }.to raise_error(ArgumentError, /does not support blocks/) end end describe "when created as an `only_if`" do describe "after running a successful command given as a string" do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "true") end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be_truthy end end describe "after running a negative/false command given as a string" do before do @status.send("success?=", false) @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false") end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be_falsey end end describe "after running a successful command given as an array" do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource, ["true"]) end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be true end end describe "after running a negative/false command given as an array" do before do @status.send("success?=", false) @conditional = Chef::Resource::Conditional.only_if(@parent_resource, ["false"]) end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be false end end describe 'after running a command which timed out' do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false") allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout) end it 'indicates that resource convergence should not continue' do expect(@conditional.continue?).to be_falsey end it 'should log a warning' do expect(Chef::Log).to receive(:warn).with("Command 'false' timed out") @conditional.continue? end end describe "after running a block that returns a truthy value" do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { Object.new } end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be_truthy end end describe "after running a block that returns a falsey value" do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { nil } end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be_falsey end end end describe "when created as a `not_if`" do describe "after running a successful/true command given as a string" do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "true") end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be_falsey end end describe "after running a failed/false command given as a string" do before do @status.send("success?=", false) @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false") end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be_truthy end end describe "after running a successful/true command given as an array" do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource, ["true"]) end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be false end end describe "after running a failed/false command given as an array" do before do @status.send("success?=", false) @conditional = Chef::Resource::Conditional.not_if(@parent_resource, ["false"]) end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be true end end describe 'after running a command which timed out' do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false") allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout) end it 'indicates that resource convergence should continue' do expect(@conditional.continue?).to be_truthy end it 'should log a warning' do expect(Chef::Log).to receive(:warn).with("Command 'false' timed out") @conditional.continue? end end describe "after running a block that returns a truthy value" do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { Object.new } end it "indicates that resource convergence should not continue" do expect(@conditional.continue?).to be_falsey end end describe "after running a block that returns a falsey value" do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { nil } end it "indicates that resource convergence should continue" do expect(@conditional.continue?).to be_truthy end end end end chef-12.3.0/spec/unit/resource/yum_package_spec.rb0000644000004100000410000000473312520074675022135 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' require 'support/shared/unit/resource/static_provider_resolution' describe Chef::Resource::YumPackage, "initialize" do static_provider_resolution( resource: Chef::Resource::YumPackage, provider: Chef::Provider::Package::Yum, name: :yum_package, action: :install, os: 'linux', platform_family: 'rhel', ) end describe Chef::Resource::YumPackage, "arch" do before(:each) do @resource = Chef::Resource::YumPackage.new("foo") end it "should set the arch variable to whatever is passed in" do @resource.arch("i386") expect(@resource.arch).to eql("i386") end end describe Chef::Resource::YumPackage, "flush_cache" do before(:each) do @resource = Chef::Resource::YumPackage.new("foo") end it "should default the flush timing to false" do flush_hash = { :before => false, :after => false } expect(@resource.flush_cache).to eq(flush_hash) end it "should allow you to set the flush timing with an array" do flush_array = [ :before, :after ] flush_hash = { :before => true, :after => true } @resource.flush_cache(flush_array) expect(@resource.flush_cache).to eq(flush_hash) end it "should allow you to set the flush timing with a hash" do flush_hash = { :before => true, :after => true } @resource.flush_cache(flush_hash) expect(@resource.flush_cache).to eq(flush_hash) end end describe Chef::Resource::YumPackage, "allow_downgrade" do before(:each) do @resource = Chef::Resource::YumPackage.new("foo") end it "should allow you to specify whether allow_downgrade is true or false" do expect { @resource.allow_downgrade true }.not_to raise_error expect { @resource.allow_downgrade false }.not_to raise_error expect { @resource.allow_downgrade "monkey" }.to raise_error(ArgumentError) end end chef-12.3.0/spec/unit/resource/ruby_block_spec.rb0000644000004100000410000000341012520074675021772 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Tyler Cloke () # 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' describe Chef::Resource::RubyBlock do before(:each) do @resource = Chef::Resource::RubyBlock.new("fakey_fakerton") end it "should create a new Chef::Resource::RubyBlock" do expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::RubyBlock) end it "should have a default action of 'create'" do expect(@resource.action).to eql("run") end it "should have a resource name of :ruby_block" do expect(@resource.resource_name).to eql(:ruby_block) end it "should accept a ruby block/proc/.. for the 'block' parameter" do expect(@resource.block do "foo" end.call).to eql("foo") end it "allows the action to be 'create'" do @resource.action :create expect(@resource.action).to eq([:create]) end describe "when it has been initialized with block code" do before do @resource.block_name("puts 'harrrr'") end it "returns the block as its identity" do expect(@resource.identity).to eq("puts 'harrrr'") end end end chef-12.3.0/spec/unit/config_spec.rb0000644000004100000410000005022012520074675017256 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Kyle Goodwin () # 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' require 'chef/exceptions' require 'chef/util/path_helper' describe Chef::Config do describe "config attribute writer: chef_server_url" do before do Chef::Config.chef_server_url = "https://junglist.gen.nz" end it "sets the server url" do expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz") end context "when the url has a leading space" do before do Chef::Config.chef_server_url = " https://junglist.gen.nz" end it "strips the space from the url when setting" do expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz") end end context "when the url is a frozen string" do before do Chef::Config.chef_server_url = " https://junglist.gen.nz".freeze end it "strips the space from the url when setting without raising an error" do expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz") end end end describe "when configuring formatters" do # if TTY and not(force-logger) # formatter = configured formatter or default formatter # formatter goes to STDOUT/ERR # if log file is writeable # log level is configured level or info # log location is file # else # log level is warn # log location is STDERR # end # elsif not(TTY) and force formatter # formatter = configured formatter or default formatter # if log_location specified # formatter goes to log_location # else # formatter goes to STDOUT/ERR # end # else # formatter = "null" # log_location = configured-value or defualt # log_level = info or defualt # end # it "has an empty list of formatters by default" do expect(Chef::Config.formatters).to eq([]) end it "configures a formatter with a short name" do Chef::Config.add_formatter(:doc) expect(Chef::Config.formatters).to eq([[:doc, nil]]) end it "configures a formatter with a file output" do Chef::Config.add_formatter(:doc, "/var/log/formatter.log") expect(Chef::Config.formatters).to eq([[:doc, "/var/log/formatter.log"]]) end end describe "class method: manage_secret_key" do before do allow(Chef::FileCache).to receive(:load).and_return(true) allow(Chef::FileCache).to receive(:has_key?).with("chef_server_cookie_id").and_return(false) end it "should generate and store a chef server cookie id" do expect(Chef::FileCache).to receive(:store).with("chef_server_cookie_id", /\w{40}/).and_return(true) Chef::Config.manage_secret_key end describe "when the filecache has a chef server cookie id key" do before do allow(Chef::FileCache).to receive(:has_key?).with("chef_server_cookie_id").and_return(true) end it "should not generate and store a chef server cookie id" do expect(Chef::FileCache).not_to receive(:store).with("chef_server_cookie_id", /\w{40}/) Chef::Config.manage_secret_key end end end [ false, true ].each do |is_windows| context "On #{is_windows ? 'Windows' : 'Unix'}" do def to_platform(*args) Chef::Config.platform_specific_path(*args) end before :each do allow(Chef::Platform).to receive(:windows?).and_return(is_windows) end describe "class method: platform_specific_path" do if is_windows it "should return a windows path on windows systems" do path = "/etc/chef/cookbooks" allow(Chef::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' }) # match on a regex that looks for the base path with an optional # system drive at the beginning (c:) # system drive is not hardcoded b/c it can change and b/c it is not present on linux systems expect(Chef::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks") end else it "should return given path on non-windows systems" do path = "/etc/chef/cookbooks" expect(Chef::Config.platform_specific_path(path)).to eq("/etc/chef/cookbooks") end end end describe "default values" do let :primary_cache_path do if is_windows "#{Chef::Config.env['SYSTEMDRIVE']}\\chef" else "/var/chef" end end let :secondary_cache_path do if is_windows "#{Chef::Config[:user_home]}\\.chef" else "#{Chef::Config[:user_home]}/.chef" end end before do if is_windows allow(Chef::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' }) Chef::Config[:user_home] = 'C:\Users\charlie' else Chef::Config[:user_home] = '/Users/charlie' end allow(Chef::Config).to receive(:path_accessible?).and_return(false) end describe "Chef::Config[:cache_path]" do context "when /var/chef exists and is accessible" do it "defaults to /var/chef" do allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true) expect(Chef::Config[:cache_path]).to eq(primary_cache_path) end end context "when /var/chef does not exist and /var is accessible" do it "defaults to /var/chef" do allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false) allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(true) expect(Chef::Config[:cache_path]).to eq(primary_cache_path) end end context "when /var/chef does not exist and /var is not accessible" do it "defaults to $HOME/.chef" do allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false) allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(false) expect(Chef::Config[:cache_path]).to eq(secondary_cache_path) end end context "when /var/chef exists and is not accessible" do it "defaults to $HOME/.chef" do allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(true) allow(File).to receive(:readable?).with(to_platform("/var/chef")).and_return(true) allow(File).to receive(:writable?).with(to_platform("/var/chef")).and_return(false) expect(Chef::Config[:cache_path]).to eq(secondary_cache_path) end end context "when chef is running in local mode" do before do Chef::Config.local_mode = true end context "and config_dir is /a/b/c" do before do Chef::Config.config_dir to_platform('/a/b/c') end it "cache_path is /a/b/c/local-mode-cache" do expect(Chef::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache')) end end context "and config_dir is /a/b/c/" do before do Chef::Config.config_dir to_platform('/a/b/c/') end it "cache_path is /a/b/c/local-mode-cache" do expect(Chef::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache')) end end end end it "Chef::Config[:file_backup_path] defaults to /var/chef/backup" do allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path) backup_path = is_windows ? "#{primary_cache_path}\\backup" : "#{primary_cache_path}/backup" expect(Chef::Config[:file_backup_path]).to eq(backup_path) end it "Chef::Config[:ssl_verify_mode] defaults to :verify_peer" do expect(Chef::Config[:ssl_verify_mode]).to eq(:verify_peer) end it "Chef::Config[:ssl_ca_path] defaults to nil" do expect(Chef::Config[:ssl_ca_path]).to be_nil end # TODO can this be removed? if !is_windows it "Chef::Config[:ssl_ca_file] defaults to nil" do expect(Chef::Config[:ssl_ca_file]).to be_nil end end it "Chef::Config[:data_bag_path] defaults to /var/chef/data_bags" do allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path) data_bag_path = is_windows ? "#{primary_cache_path}\\data_bags" : "#{primary_cache_path}/data_bags" expect(Chef::Config[:data_bag_path]).to eq(data_bag_path) end it "Chef::Config[:environment_path] defaults to /var/chef/environments" do allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path) environment_path = is_windows ? "#{primary_cache_path}\\environments" : "#{primary_cache_path}/environments" expect(Chef::Config[:environment_path]).to eq(environment_path) end describe "setting the config dir" do context "when the config file is /etc/chef/client.rb" do before do Chef::Config.config_file = to_platform("/etc/chef/client.rb") end it "config_dir is /etc/chef" do expect(Chef::Config.config_dir).to eq(to_platform("/etc/chef")) end context "and chef is running in local mode" do before do Chef::Config.local_mode = true end it "config_dir is /etc/chef" do expect(Chef::Config.config_dir).to eq(to_platform("/etc/chef")) end end context "when config_dir is set to /other/config/dir/" do before do Chef::Config.config_dir = to_platform("/other/config/dir/") end it "yields the explicit value" do expect(Chef::Config.config_dir).to eq(to_platform("/other/config/dir/")) end end end context "when the user's home dir is /home/charlie/" do before do Chef::Config.user_home = to_platform("/home/charlie") end it "config_dir is /home/charlie/.chef/" do expect(Chef::Config.config_dir).to eq(Chef::Util::PathHelper.join(to_platform("/home/charlie/.chef"), '')) end context "and chef is running in local mode" do before do Chef::Config.local_mode = true end it "config_dir is /home/charlie/.chef/" do expect(Chef::Config.config_dir).to eq(Chef::Util::PathHelper.join(to_platform("/home/charlie/.chef"), '')) end end end end if is_windows describe "finding the windows embedded dir" do let(:default_config_location) { "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" } let(:alternate_install_location) { "c:/my/alternate/install/place/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" } let(:non_omnibus_location) { "c:/my/dev/stuff/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" } let(:default_ca_file) { "c:/opscode/chef/embedded/ssl/certs/cacert.pem" } it "finds the embedded dir in the default location" do allow(Chef::Config).to receive(:_this_file).and_return(default_config_location) expect(Chef::Config.embedded_dir).to eq("c:/opscode/chef/embedded") end it "finds the embedded dir in a custom install location" do allow(Chef::Config).to receive(:_this_file).and_return(alternate_install_location) expect(Chef::Config.embedded_dir).to eq("c:/my/alternate/install/place/chef/embedded") end it "doesn't error when not in an omnibus install" do allow(Chef::Config).to receive(:_this_file).and_return(non_omnibus_location) expect(Chef::Config.embedded_dir).to be_nil end it "sets the ssl_ca_cert path if the cert file is available" do allow(Chef::Config).to receive(:_this_file).and_return(default_config_location) allow(File).to receive(:exist?).with(default_ca_file).and_return(true) expect(Chef::Config.ssl_ca_file).to eq(default_ca_file) end end end end describe "Chef::Config[:user_home]" do it "should set when HOME is provided" do expected = to_platform("/home/kitten") allow(Chef::Util::PathHelper).to receive(:home).and_return(expected) expect(Chef::Config[:user_home]).to eq(expected) end it "falls back to the current working directory when HOME and USERPROFILE is not set" do allow(Chef::Util::PathHelper).to receive(:home).and_return(nil) expect(Chef::Config[:user_home]).to eq(Dir.pwd) end end describe "Chef::Config[:encrypted_data_bag_secret]" do let(:db_secret_default_path){ to_platform("/etc/chef/encrypted_data_bag_secret") } before do allow(File).to receive(:exist?).with(db_secret_default_path).and_return(secret_exists) end context "/etc/chef/encrypted_data_bag_secret exists" do let(:secret_exists) { true } it "sets the value to /etc/chef/encrypted_data_bag_secret" do expect(Chef::Config[:encrypted_data_bag_secret]).to eq db_secret_default_path end end context "/etc/chef/encrypted_data_bag_secret does not exist" do let(:secret_exists) { false } it "sets the value to nil" do expect(Chef::Config[:encrypted_data_bag_secret]).to be_nil end end end describe "Chef::Config[:event_handlers]" do it "sets a event_handlers to an empty array by default" do expect(Chef::Config[:event_handlers]).to eq([]) end it "should be able to add custom handlers" do o = Object.new Chef::Config[:event_handlers] << o expect(Chef::Config[:event_handlers]).to be_include(o) end end describe "Chef::Config[:user_valid_regex]" do context "on a platform that is not Windows" do it "allows one letter usernames" do any_match = Chef::Config[:user_valid_regex].any? { |regex| regex.match('a') } expect(any_match).to be_truthy end end end describe "Chef::Config[:internal_locale]" do let(:shell_out) do double("Chef::Mixin::ShellOut double", :exitstatus => 0, :stdout => locales) end let(:locales) { locale_array.join("\n") } before do allow(Chef::Config).to receive(:shell_out_with_systems_locale!).with("locale -a").and_return(shell_out) end shared_examples_for "a suitable locale" do it "returns an English UTF-8 locale" do expect(Chef::Log).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/) expect(Chef::Log).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/) expect(Chef::Log).to_not receive(:debug).with(/No usable locale -a command found/) expect(Chef::Config.guess_internal_locale).to eq expected_locale end end context "when the result includes 'C.UTF-8'" do include_examples "a suitable locale" do let(:locale_array) { [expected_locale, "en_US.UTF-8"] } let(:expected_locale) { "C.UTF-8" } end end context "when the result includes 'en_US.UTF-8'" do include_examples "a suitable locale" do let(:locale_array) { ["en_CA.UTF-8", expected_locale, "en_NZ.UTF-8"] } let(:expected_locale) { "en_US.UTF-8" } end end context "when the result includes 'en_US.utf8'" do include_examples "a suitable locale" do let(:locale_array) { ["en_CA.utf8", "en_US.utf8", "en_NZ.utf8"] } let(:expected_locale) { "en_US.UTF-8" } end end context "when the result includes 'en.UTF-8'" do include_examples "a suitable locale" do let(:locale_array) { ["en.ISO8859-1", expected_locale] } let(:expected_locale) { "en.UTF-8" } end end context "when the result includes 'en_*.UTF-8'" do include_examples "a suitable locale" do let(:locale_array) { [expected_locale, "en_CA.UTF-8", "en_GB.UTF-8"] } let(:expected_locale) { "en_AU.UTF-8" } end end context "when the result includes 'en_*.utf8'" do include_examples "a suitable locale" do let(:locale_array) { ["en_AU.utf8", "en_CA.utf8", "en_GB.utf8"] } let(:expected_locale) { "en_AU.UTF-8" } end end context "when the result does not include 'en_*.UTF-8'" do let(:locale_array) { ["af_ZA", "af_ZA.ISO8859-1", "af_ZA.ISO8859-15", "af_ZA.UTF-8"] } it "should fall back to C locale" do expect(Chef::Log).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.") expect(Chef::Config.guess_internal_locale).to eq 'C' end end context "on error" do let(:locale_array) { [] } before do allow(Chef::Config).to receive(:shell_out_with_systems_locale!).and_raise("THIS IS AN ERROR") end it "should default to 'en_US.UTF-8'" do if is_windows expect(Chef::Log).to receive(:debug).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.") else expect(Chef::Log).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.") end expect(Chef::Config.guess_internal_locale).to eq "en_US.UTF-8" end end end end end describe "Treating deprecation warnings as errors" do context "when using our default RSpec configuration" do it "defaults to treating deprecation warnings as errors" do expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(true) end it "sets CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS environment variable" do expect(ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS']).to eq("1") end it "treats deprecation warnings as errors in child processes when testing" do # Doing a full integration test where we launch a child process is slow # and liable to break for weird reasons (bundler env stuff, etc.), so # we're just checking that the presence of the environment variable # causes treat_deprecation_warnings_as_errors to be set to true after a # config reset. Chef::Config.reset expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(true) end end context "outside of our test environment" do before do ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS') Chef::Config.reset end it "defaults to NOT treating deprecation warnings as errors" do expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(false) end end end end chef-12.3.0/spec/unit/cookbook_version_spec.rb0000644000004100000410000003374612520074675021402 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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::CookbookVersion do describe "when first created" do before do @cookbook_version = Chef::CookbookVersion.new("tatft", '/tmp/blah') end it "has a name" do expect(@cookbook_version.name).to eq('tatft') end it "has no attribute files" do expect(@cookbook_version.attribute_filenames).to be_empty end it "has no resource definition files" do expect(@cookbook_version.definition_filenames).to be_empty end it "has no cookbook files" do expect(@cookbook_version.file_filenames).to be_empty end it "has no recipe files" do expect(@cookbook_version.recipe_filenames).to be_empty end it "has no library files" do expect(@cookbook_version.library_filenames).to be_empty end it "has no LWRP resource files" do expect(@cookbook_version.resource_filenames).to be_empty end it "has no LWRP provider files" do expect(@cookbook_version.provider_filenames).to be_empty end it "has no metadata files" do expect(@cookbook_version.metadata_filenames).to be_empty end it "is not frozen" do expect(@cookbook_version).not_to be_frozen_version end it "can be frozen" do @cookbook_version.freeze_version expect(@cookbook_version).to be_frozen_version end it "has empty metadata" do expect(@cookbook_version.metadata).to eq(Chef::Cookbook::Metadata.new) end end describe "with a cookbook directory named tatft" do MD5 = /[0-9a-f]{32}/ before do @cookbook = Hash.new { |hash, key| hash[key] = [] } @cookbook_root = File.join(CHEF_SPEC_DATA, 'cb_version_cookbooks', 'tatft') # Dunno if the paths here are representitive of what is set by CookbookLoader... @cookbook[:attribute_filenames] = Dir[File.join(@cookbook_root, 'attributes', '**', '*.rb')] @cookbook[:definition_filenames] = Dir[File.join(@cookbook_root, 'definitions', '**', '*.rb')] @cookbook[:file_filenames] = Dir[File.join(@cookbook_root, 'files', '**', '*.tgz')] @cookbook[:recipe_filenames] = Dir[File.join(@cookbook_root, 'recipes', '**', '*.rb')] @cookbook[:template_filenames] = Dir[File.join(@cookbook_root, 'templates', '**', '*.erb')] @cookbook[:library_filenames] = Dir[File.join(@cookbook_root, 'libraries', '**', '*.rb')] @cookbook[:resource_filenames] = Dir[File.join(@cookbook_root, 'resources', '**', '*.rb')] @cookbook[:provider_filenames] = Dir[File.join(@cookbook_root, 'providers', '**', '*.rb')] @cookbook[:root_filenames] = Array(File.join(@cookbook_root, 'README.rdoc')) @cookbook[:metadata_filenames] = Array(File.join(@cookbook_root, 'metadata.json')) end describe "and a cookbook with the same name" do before do # Currently the cookbook loader finds all the files then tells CookbookVersion # where they are. @cookbook_version = Chef::CookbookVersion.new('tatft', @cookbook_root) @cookbook_version.attribute_filenames = @cookbook[:attribute_filenames] @cookbook_version.definition_filenames = @cookbook[:definition_filenames] @cookbook_version.recipe_filenames = @cookbook[:recipe_filenames] @cookbook_version.template_filenames = @cookbook[:template_filenames] @cookbook_version.file_filenames = @cookbook[:file_filenames] @cookbook_version.library_filenames = @cookbook[:library_filenames] @cookbook_version.resource_filenames = @cookbook[:resource_filenames] @cookbook_version.provider_filenames = @cookbook[:provider_filenames] @cookbook_version.root_filenames = @cookbook[:root_filenames] @cookbook_version.metadata_filenames = @cookbook[:metadata_filenames] # Used to test file-specificity related file lookups @node = Chef::Node.new @node.set[:platform] = "ubuntu" @node.set[:platform_version] = "13.04" @node.name("testing") end it "determines whether a template is available for a given node" do expect(@cookbook_version).to have_template_for_node(@node, "configuration.erb") expect(@cookbook_version).not_to have_template_for_node(@node, "missing.erb") end it "determines whether a cookbook_file is available for a given node" do expect(@cookbook_version).to have_cookbook_file_for_node(@node, "giant_blob.tgz") expect(@cookbook_version).not_to have_cookbook_file_for_node(@node, "missing.txt") end describe "raises an error when attempting to load a missing cookbook_file and" do before do node = Chef::Node.new.tap do |n| n.name("sample.node") n.automatic_attrs[:fqdn] = "sample.example.com" n.automatic_attrs[:platform] = "ubuntu" n.automatic_attrs[:platform_version] = "10.04" end @attempt_to_load_file = lambda { @cookbook_version.preferred_manifest_record(node, :files, "no-such-thing.txt") } end it "describes the cookbook and version" do useful_explanation = Regexp.new(Regexp.escape("Cookbook 'tatft' (0.0.0) does not contain")) expect(@attempt_to_load_file).to raise_error(Chef::Exceptions::FileNotFound, useful_explanation) end it "lists suggested places to look" do useful_explanation = Regexp.new(Regexp.escape("files/default/no-such-thing.txt")) expect(@attempt_to_load_file).to raise_error(Chef::Exceptions::FileNotFound, useful_explanation) end end end end describe 'with a cookbook directory named cookbook2 that has unscoped files' do before do @cookbook = Hash.new { |hash, key| hash[key] = [] } @cookbook_root = File.join(CHEF_SPEC_DATA, 'cb_version_cookbooks', 'cookbook2') # Dunno if the paths here are representitive of what is set by CookbookLoader... @cookbook[:attribute_filenames] = Dir[File.join(@cookbook_root, 'attributes', '**', '*.rb')] @cookbook[:definition_filenames] = Dir[File.join(@cookbook_root, 'definitions', '**', '*.rb')] @cookbook[:file_filenames] = Dir[File.join(@cookbook_root, 'files', '**', '*.*')] @cookbook[:recipe_filenames] = Dir[File.join(@cookbook_root, 'recipes', '**', '*.rb')] @cookbook[:template_filenames] = Dir[File.join(@cookbook_root, 'templates', '**', '*.*')] @cookbook[:library_filenames] = Dir[File.join(@cookbook_root, 'libraries', '**', '*.rb')] @cookbook[:resource_filenames] = Dir[File.join(@cookbook_root, 'resources', '**', '*.rb')] @cookbook[:provider_filenames] = Dir[File.join(@cookbook_root, 'providers', '**', '*.rb')] @cookbook[:root_filenames] = Array(File.join(@cookbook_root, 'README.rdoc')) @cookbook[:metadata_filenames] = Array(File.join(@cookbook_root, 'metadata.json')) @cookbook_version = Chef::CookbookVersion.new('cookbook2', @cookbook_root) @cookbook_version.attribute_filenames = @cookbook[:attribute_filenames] @cookbook_version.definition_filenames = @cookbook[:definition_filenames] @cookbook_version.recipe_filenames = @cookbook[:recipe_filenames] @cookbook_version.template_filenames = @cookbook[:template_filenames] @cookbook_version.file_filenames = @cookbook[:file_filenames] @cookbook_version.library_filenames = @cookbook[:library_filenames] @cookbook_version.resource_filenames = @cookbook[:resource_filenames] @cookbook_version.provider_filenames = @cookbook[:provider_filenames] @cookbook_version.root_filenames = @cookbook[:root_filenames] @cookbook_version.metadata_filenames = @cookbook[:metadata_filenames] # Used to test file-specificity related file lookups @node = Chef::Node.new @node.set[:platform] = "ubuntu" @node.set[:platform_version] = "13.04" @node.name("testing") end it "should see a template" do expect(@cookbook_version).to have_template_for_node(@node, "test.erb") end it "should see a template using an array lookup" do expect(@cookbook_version).to have_template_for_node(@node, ["test.erb"]) end it "should see a template using an array lookup with non-existent elements" do expect(@cookbook_version).to have_template_for_node(@node, ["missing.txt", "test.erb"]) end it "should see a file" do expect(@cookbook_version).to have_cookbook_file_for_node(@node, "test.txt") end it "should see a file using an array lookup" do expect(@cookbook_version).to have_cookbook_file_for_node(@node, ["test.txt"]) end it "should see a file using an array lookup with non-existent elements" do expect(@cookbook_version).to have_cookbook_file_for_node(@node, ["missing.txt", "test.txt"]) end it "should not see a non-existent template" do expect(@cookbook_version).not_to have_template_for_node(@node, "missing.erb") end it "should not see a non-existent template using an array lookup" do expect(@cookbook_version).not_to have_template_for_node(@node, ["missing.erb"]) end it "should not see a non-existent file" do expect(@cookbook_version).not_to have_cookbook_file_for_node(@node, "missing.txt") end it "should not see a non-existent file using an array lookup" do expect(@cookbook_version).not_to have_cookbook_file_for_node(@node, ["missing.txt"]) end end describe "<=>" do it "should sort based on the version number" do examples = [ # smaller, larger ["1.0", "2.0"], ["1.2.3", "1.2.4"], ["1.2.3", "1.3.0"], ["1.2.3", "1.3"], ["1.2.3", "2.1.1"], ["1.2.3", "2.1"], ["1.2", "1.2.4"], ["1.2", "1.3.0"], ["1.2", "1.3"], ["1.2", "2.1.1"], ["1.2", "2.1"] ] examples.each do |smaller, larger| sm = Chef::CookbookVersion.new("foo", '/tmp/blah') lg = Chef::CookbookVersion.new("foo", '/tmp/blah') sm.version = smaller lg.version = larger expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end it "should equate versions 1.2 and 1.2.0" do a = Chef::CookbookVersion.new("foo", '/tmp/blah') b = Chef::CookbookVersion.new("foo", '/tmp/blah') a.version = "1.2" b.version = "1.2.0" expect(a).to eq(b) end it "should not allow you to sort cookbooks with different names" do apt = Chef::CookbookVersion.new "apt", '/tmp/blah' apt.version = "1.0" god = Chef::CookbookVersion.new "god", '/tmp/blah' god.version = "2.0" expect {apt <=> god}.to raise_error(Chef::Exceptions::CookbookVersionNameMismatch) end end describe "when you set a version" do before do @cbv = Chef::CookbookVersion.new("version validation", '/tmp/blah') end it "should accept valid cookbook versions" do good_versions = %w(1.2 1.2.3 1000.80.50000 0.300.25) good_versions.each do |v| @cbv.version = v end end it "should raise InvalidVersion for bad cookbook versions" do bad_versions = ["1.2.3.4", "1.2.a4", "1", "a", "1.2 3", "1.2 a", "1 2 3", "1-2-3", "1_2_3", "1.2_3", "1.2-3"] the_error = Chef::Exceptions::InvalidCookbookVersion bad_versions.each do |v| expect {@cbv.version = v}.to raise_error(the_error) end end end describe "when deprecation warnings are errors" do subject(:cbv) { Chef::CookbookVersion.new("version validation", '/tmp/blah') } describe "HTTP Resource behaviors", pending: "will be deprected when CookbookManifest API is stablized" do it "errors on #save_url" do expect { cbv.save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end it "errors on #force_save_url" do expect { cbv.force_save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end it "errors on #to_hash" do expect { cbv.to_hash }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end it "errors on #to_json" do expect { cbv.to_json }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end end it "errors on #status and #status=" do expect { cbv.status = :wat }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) expect { cbv.status }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end end describe "deprecated features" do subject(:cbv) { Chef::CookbookVersion.new("tatft", '/tmp/blah').tap { |c| c.version = "1.2.3" } } before do Chef::Config[:treat_deprecation_warnings_as_errors] = false end it "gives a save URL for the standard cookbook API" do expect(cbv.save_url).to eq("cookbooks/tatft/1.2.3") end it "gives a force save URL for the standard cookbook API" do expect(cbv.force_save_url).to eq("cookbooks/tatft/1.2.3?force=true") end it "is \"ready\"" do # WTF is this? what are the valid states? and why aren't they set with encapsulating methods? # [Dan 15-Jul-2010] expect(cbv.status).to eq(:ready) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { Chef::CookbookVersion.new("tatft", '/tmp/blah') } end end end chef-12.3.0/spec/unit/environment_spec.rb0000644000004100000410000004432112520074675020362 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Seth Falcon () # Author:: John Keiser () # Author:: Kyle Goodwin () # Copyright:: Copyright 2010-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 'spec_helper' require 'chef/environment' describe Chef::Environment do before(:each) do @environment = Chef::Environment.new end describe "initialize" do it "should be a Chef::Environment" do expect(@environment).to be_a_kind_of(Chef::Environment) end end describe "name" do it "should let you set the name to a string" do expect(@environment.name("production")).to eq("production") end it "should return the current name" do @environment.name("production") expect(@environment.name).to eq("production") end it "should not accept spaces" do expect { @environment.name("production environment") }.to raise_error(ArgumentError) end it "should not accept anything but strings" do expect { @environment.name(Array.new) }.to raise_error(ArgumentError) expect { @environment.name(Hash.new) }.to raise_error(ArgumentError) expect { @environment.name(2) }.to raise_error(ArgumentError) end end describe "description" do it "should let you set the description to a string" do expect(@environment.description("this is my test environment")).to eq("this is my test environment") end it "should return the correct description" do @environment.description("I like running tests") expect(@environment.description).to eq("I like running tests") end it "should not accept anything but strings" do expect { @environment.description(Array.new) }.to raise_error(ArgumentError) expect { @environment.description(Hash.new) }.to raise_error(ArgumentError) expect { @environment.description(42) }.to raise_error(ArgumentError) end end describe "default attributes" do it "should let you set the attributes hash explicitly" do expect(@environment.default_attributes({ :one => 'two' })).to eq({ :one => 'two' }) end it "should let you return the attributes hash" do @environment.default_attributes({ :one => 'two' }) expect(@environment.default_attributes).to eq({ :one => 'two' }) end it "should throw an ArgumentError if we aren't a kind of hash" do expect { @environment.default_attributes(Array.new) }.to raise_error(ArgumentError) end end describe "override attributes" do it "should let you set the attributes hash explicitly" do expect(@environment.override_attributes({ :one => 'two' })).to eq({ :one => 'two' }) end it "should let you return the attributes hash" do @environment.override_attributes({ :one => 'two' }) expect(@environment.override_attributes).to eq({ :one => 'two' }) end it "should throw an ArgumentError if we aren't a kind of hash" do expect { @environment.override_attributes(Array.new) }.to raise_error(ArgumentError) end end describe "cookbook_versions" do before(:each) do @cookbook_versions = { "apt" => "= 1.0.0", "god" => "= 2.0.0", "apache2" => "= 4.2.0" } end it "should let you set the cookbook versions in a hash" do expect(@environment.cookbook_versions(@cookbook_versions)).to eq(@cookbook_versions) end it "should return the cookbook versions" do @environment.cookbook_versions(@cookbook_versions) expect(@environment.cookbook_versions).to eq(@cookbook_versions) end it "should not accept anything but a hash" do expect { @environment.cookbook_versions("I am a string!") }.to raise_error(ArgumentError) expect { @environment.cookbook_versions(Array.new) }.to raise_error(ArgumentError) expect { @environment.cookbook_versions(42) }.to raise_error(ArgumentError) end it "should validate the hash" do expect(Chef::Environment).to receive(:validate_cookbook_versions).with(@cookbook_versions).and_return true @environment.cookbook_versions(@cookbook_versions) end end describe "cookbook" do it "should set the version of the cookbook in the cookbook_versions hash" do @environment.cookbook("apt", "~> 1.2.3") expect(@environment.cookbook_versions["apt"]).to eq("~> 1.2.3") end it "should validate the cookbook version it is passed" do expect(Chef::Environment).to receive(:validate_cookbook_version).with(">= 1.2.3").and_return true @environment.cookbook("apt", ">= 1.2.3") end end describe "update_from!" do before(:each) do @environment.name("prod") @environment.description("this is prod") @environment.cookbook_versions({ "apt" => "= 1.2.3" }) @example = Chef::Environment.new @example.name("notevenprod") @example.description("this is pre-prod") @example.cookbook_versions({ "apt" => "= 2.3.4" }) end it "should update everything but name" do @environment.update_from!(@example) expect(@environment.name).to eq("prod") expect(@environment.description).to eq(@example.description) expect(@environment.cookbook_versions).to eq(@example.cookbook_versions) end end describe "to_hash" do before(:each) do @environment.name("spec") @environment.description("Where we run the spec tests") @environment.cookbook_versions({:apt => "= 1.2.3"}) @hash = @environment.to_hash end %w{name description cookbook_versions}.each do |t| it "should include '#{t}'" do expect(@hash[t]).to eq(@environment.send(t.to_sym)) end end it "should include 'json_class'" do expect(@hash["json_class"]).to eq("Chef::Environment") end it "should include 'chef_type'" do expect(@hash["chef_type"]).to eq("environment") end end describe "to_json" do before(:each) do @environment.name("spec") @environment.description("Where we run the spec tests") @environment.cookbook_versions({:apt => "= 1.2.3"}) @json = @environment.to_json end %w{name description cookbook_versions}.each do |t| it "should include '#{t}'" do expect(@json).to match(/"#{t}":#{Regexp.escape(Chef::JSONCompat.to_json(@environment.send(t.to_sym)))}/) end end it "should include 'json_class'" do expect(@json).to match(/"json_class":"Chef::Environment"/) end it "should include 'chef_type'" do expect(@json).to match(/"chef_type":"environment"/) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @environment } end end describe "from_json" do before(:each) do @data = { "name" => "production", "description" => "We are productive", "cookbook_versions" => { "apt" => "= 1.2.3", "god" => ">= 4.2.0", "apache2" => "= 2.0.0" }, "json_class" => "Chef::Environment", "chef_type" => "environment" } @environment = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@data)) end it "should return a Chef::Environment" do expect(@environment).to be_a_kind_of(Chef::Environment) end %w{name description cookbook_versions}.each do |t| it "should match '#{t}'" do expect(@environment.send(t.to_sym)).to eq(@data[t]) end end end describe "self.validate_cookbook_versions" do before(:each) do @cookbook_versions = { "apt" => "= 1.0.0", "god" => "= 2.0.0", "apache2" => "= 4.2.0" } end it "should validate the version string of each cookbook" do @cookbook_versions.each do |cookbook, version| expect(Chef::Environment).to receive(:validate_cookbook_version).with(version).and_return true end Chef::Environment.validate_cookbook_versions(@cookbook_versions) end it "should return false if anything other than a hash is passed as the argument" do expect(Chef::Environment.validate_cookbook_versions(Array.new)).to eq(false) expect(Chef::Environment.validate_cookbook_versions(42)).to eq(false) expect(Chef::Environment.validate_cookbook_versions(Chef::CookbookVersion.new("meta"))).to eq(false) expect(Chef::Environment.validate_cookbook_versions("cookbook => 1.2.3")).to eq(false) end end describe "self.validate_cookbook_version" do it "should validate correct version numbers" do expect(Chef::Environment.validate_cookbook_version("= 1.2.3")).to eq(true) expect(Chef::Environment.validate_cookbook_version("=1.2.3")).to eq(true) expect(Chef::Environment.validate_cookbook_version(">= 0.0.3")).to eq(true) expect(Chef::Environment.validate_cookbook_version(">=0.0.3")).to eq(true) # A lone version is allowed, interpreted as implicit '=' expect(Chef::Environment.validate_cookbook_version("1.2.3")).to eq(true) end it "should return false when an invalid version is given" do expect(Chef::Environment.validate_cookbook_version(Chef::CookbookVersion.new("meta"))).to eq(false) expect(Chef::Environment.validate_cookbook_version("= 1.2.3a")).to eq(false) expect(Chef::Environment.validate_cookbook_version("=1.2.3a")).to eq(false) expect(Chef::Environment.validate_cookbook_version("= 1")).to eq(false) expect(Chef::Environment.validate_cookbook_version("=1")).to eq(false) expect(Chef::Environment.validate_cookbook_version("= a")).to eq(false) expect(Chef::Environment.validate_cookbook_version("=a")).to eq(false) expect(Chef::Environment.validate_cookbook_version("= 1.2.3.4")).to eq(false) expect(Chef::Environment.validate_cookbook_version("=1.2.3.4")).to eq(false) end describe "in solo mode" do before do Chef::Config[:solo] = true end after do Chef::Config[:solo] = false end it "should raise and exception" do expect { Chef::Environment.validate_cookbook_version("= 1.2.3.4") }.to raise_error Chef::Exceptions::IllegalVersionConstraint, "Environment cookbook version constraints not allowed in chef-solo" end end end describe "when updating from a parameter hash" do before do @environment = Chef::Environment.new end it "updates the name from parameters[:name]" do @environment.update_from_params(:name => "kurrupt") expect(@environment.name).to eq("kurrupt") end it "validates the name given in the params" do expect(@environment.update_from_params(:name => "@$%^&*()")).to be_falsey expect(@environment.invalid_fields[:name]).to eq(%q|Option name's value @$%^&*() does not match regular expression /^[\-[:alnum:]_]+$/|) end it "updates the description from parameters[:description]" do @environment.update_from_params(:description => "wow, writing your own object mapper is kinda painful") expect(@environment.description).to eq("wow, writing your own object mapper is kinda painful") end it "updates cookbook version constraints from the hash in parameters[:cookbook_version_constraints]" do # NOTE: I'm only choosing this (admittedly weird) structure for the hash b/c the better more obvious # one, i.e, {:cookbook_version_constraints => {COOKBOOK_NAME => CONSTRAINT}} is difficult to implement # the way merb does params params = {:name=>"superbowl", :cookbook_version => {"0" => "apache2 ~> 1.0.0", "1" => "nginx < 2.0.0"}} @environment.update_from_params(params) expect(@environment.cookbook_versions).to eq({"apache2" => "~> 1.0.0", "nginx" => "< 2.0.0"}) end it "validates the cookbook constraints" do params = {:cookbook_version => {"0" => "apache2 >>> 1.0.0"}} expect(@environment.update_from_params(params)).to be_falsey err_msg = @environment.invalid_fields[:cookbook_version]["0"] expect(err_msg).to eq("apache2 >>> 1.0.0 is not a valid cookbook constraint") end it "is not valid if the name is not present" do expect(@environment.validate_required_attrs_present).to be_falsey expect(@environment.invalid_fields[:name]).to eq("name cannot be empty") end it "is not valid after updating from params if the name is not present" do expect(@environment.update_from_params({})).to be_falsey expect(@environment.invalid_fields[:name]).to eq("name cannot be empty") end it "updates default attributes from a JSON string in params[:attributes]" do @environment.update_from_params(:name => "fuuu", :default_attributes => %q|{"fuuu":"RAGE"}|) expect(@environment.default_attributes).to eq({"fuuu" => "RAGE"}) end it "updates override attributes from a JSON string in params[:attributes]" do @environment.update_from_params(:name => "fuuu", :override_attributes => %q|{"foo":"override"}|) expect(@environment.override_attributes).to eq({"foo" => "override"}) end end describe "api model" do before(:each) do @rest = double("Chef::REST") allow(Chef::REST).to receive(:new).and_return(@rest) @query = double("Chef::Search::Query") allow(Chef::Search::Query).to receive(:new).and_return(@query) end describe "list" do describe "inflated" do it "should return a hash of environment names and objects" do e1 = double("Chef::Environment", :name => "one") expect(@query).to receive(:search).with(:environment).and_yield(e1) r = Chef::Environment.list(true) expect(r["one"]).to eq(e1) end end it "should return a hash of environment names and urls" do expect(@rest).to receive(:get_rest).and_return({ "one" => "http://foo" }) r = Chef::Environment.list expect(r["one"]).to eq("http://foo") end end end describe "when loading" do describe "in solo mode" do before do Chef::Config[:solo] = true Chef::Config[:environment_path] = '/var/chef/environments' end after do Chef::Config[:solo] = false end it "should get the environment from the environment_path" do expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.json')).and_return(false) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.rb')).exactly(2).times.and_return(true) expect(File).to receive(:readable?).with(File.join(Chef::Config[:environment_path], 'foo.rb')).and_return(true) role_dsl="name \"foo\"\ndescription \"desc\"\n" expect(IO).to receive(:read).with(File.join(Chef::Config[:environment_path], 'foo.rb')).and_return(role_dsl) Chef::Environment.load('foo') end it "should return a Chef::Environment object from JSON" do expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.json')).and_return(true) environment_hash = { "name" => "foo", "default_attributes" => { "foo" => { "bar" => 1 } }, "json_class" => "Chef::Environment", "description" => "desc", "chef_type" => "environment" } expect(IO).to receive(:read).with(File.join(Chef::Config[:environment_path], 'foo.json')).and_return(Chef::JSONCompat.to_json(environment_hash)) environment = Chef::Environment.load('foo') expect(environment).to be_a_kind_of(Chef::Environment) expect(environment.name).to eq(environment_hash['name']) expect(environment.description).to eq(environment_hash['description']) expect(environment.default_attributes).to eq(environment_hash['default_attributes']) end it "should return a Chef::Environment object from Ruby DSL" do expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.json')).and_return(false) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.rb')).exactly(2).times.and_return(true) expect(File).to receive(:readable?).with(File.join(Chef::Config[:environment_path], 'foo.rb')).and_return(true) role_dsl="name \"foo\"\ndescription \"desc\"\n" expect(IO).to receive(:read).with(File.join(Chef::Config[:environment_path], 'foo.rb')).and_return(role_dsl) environment = Chef::Environment.load('foo') expect(environment).to be_a_kind_of(Chef::Environment) expect(environment.name).to eq('foo') expect(environment.description).to eq('desc') end it 'should raise an error if the configured environment_path is invalid' do expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(false) expect { Chef::Environment.load('foo') }.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid" end it 'should raise an error if the file does not exist' do expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(true) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.json')).and_return(false) expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], 'foo.rb')).and_return(false) expect { Chef::Environment.load('foo') }.to raise_error Chef::Exceptions::EnvironmentNotFound, "Environment 'foo' could not be loaded from disk" end end end end chef-12.3.0/spec/unit/deprecation_spec.rb0000644000004100000410000000571712520074675020321 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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/deprecation/warnings' describe Chef::Deprecation do # Support code for Chef::Deprecation def self.class_from_string(str) str.split('::').inject(Object) do |mod, class_name| mod.const_get(class_name) end end module DeprecatedMethods def deprecated_method(value) @value = value end def get_value @value end end class TestClass extend Chef::Deprecation::Warnings include DeprecatedMethods add_deprecation_warnings_for(DeprecatedMethods.instance_methods) end method_snapshot_file = File.join(CHEF_SPEC_DATA, "file-providers-method-snapshot-chef-11-4.json") method_snapshot = Chef::JSONCompat.parse(File.open(method_snapshot_file).read()) method_snapshot.each do |class_name, old_methods| class_object = class_from_string(class_name) current_methods = class_object.public_instance_methods.map(&:to_sym) it "defines all methods on #{class_object} that were available in 11.0" do old_methods.each do |old_method| expect(current_methods).to include(old_method.to_sym) end end end context 'when Chef::Config[:treat_deprecation_warnings_as_errors] is off' do before do Chef::Config[:treat_deprecation_warnings_as_errors] = false end context 'deprecation warning messages' do before(:each) do @warning_output = [ ] allow(Chef::Log).to receive(:warn) { |msg| @warning_output << msg } end it 'should be enabled for deprecated methods' do TestClass.new.deprecated_method(10) expect(@warning_output).not_to be_empty end it 'should contain stack trace' do TestClass.new.deprecated_method(10) expect(@warning_output.join("").include?(".rb")).to be_truthy end end it 'deprecated methods should still be called' do test_instance = TestClass.new test_instance.deprecated_method(10) expect(test_instance.get_value).to eq(10) end end it 'should raise when deprecation warnings are treated as errors' do # rspec should set this expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be true test_instance = TestClass.new expect { test_instance.deprecated_method(10) }.to raise_error(Chef::Exceptions::DeprecatedFeatureError) end end chef-12.3.0/spec/unit/node_spec.rb0000644000004100000410000013772412520074675016755 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'ostruct' describe Chef::Node do let(:node) { Chef::Node.new() } let(:platform_introspector) { node } it_behaves_like "a platform introspector" it "creates a node and assigns it a name" do node = Chef::Node.build('solo-node') expect(node.name).to eq('solo-node') end it "should validate the name of the node" do expect{Chef::Node.build('solo node')}.to raise_error(Chef::Exceptions::ValidationFailed) end it "should be sortable" do n1 = Chef::Node.build('alpha') n2 = Chef::Node.build('beta') n3 = Chef::Node.build('omega') expect([n3, n1, n2].sort).to eq([n1, n2, n3]) end describe "when the node does not exist on the server" do before do response = OpenStruct.new(:code => '404') exception = Net::HTTPServerException.new("404 not found", response) allow(Chef::Node).to receive(:load).and_raise(exception) node.name("created-node") end it "creates a new node for find_or_create" do allow(Chef::Node).to receive(:new).and_return(node) expect(node).to receive(:create).and_return(node) node = Chef::Node.find_or_create("created-node") expect(node.name).to eq('created-node') expect(node).to equal(node) end end describe "when the node exists on the server" do before do node.name('existing-node') allow(Chef::Node).to receive(:load).and_return(node) end it "loads the node via the REST API for find_or_create" do expect(Chef::Node.find_or_create('existing-node')).to equal(node) end end describe "run_state" do it "is an empty hash" do expect(node.run_state).to respond_to(:keys) expect(node.run_state).to be_empty end end describe "initialize" do it "should default to the '_default' chef_environment" do n = Chef::Node.new expect(n.chef_environment).to eq('_default') end end describe "name" do it "should allow you to set a name with name(something)" do expect { node.name("latte") }.not_to raise_error end it "should return the name with name()" do node.name("latte") expect(node.name).to eql("latte") end it "should always have a string for name" do expect { node.name(Hash.new) }.to raise_error(ArgumentError) end it "cannot be blank" do expect { node.name("")}.to raise_error(Chef::Exceptions::ValidationFailed) end it "should not accept name doesn't match /^[\-[:alnum:]_:.]+$/" do expect { node.name("space in it")}.to raise_error(Chef::Exceptions::ValidationFailed) end end describe "chef_environment" do it "should set an environment with chef_environment(something)" do expect { node.chef_environment("latte") }.not_to raise_error end it "should return the chef_environment with chef_environment()" do node.chef_environment("latte") expect(node.chef_environment).to eq("latte") end it "should disallow non-strings" do expect { node.chef_environment(Hash.new) }.to raise_error(ArgumentError) expect { node.chef_environment(42) }.to raise_error(ArgumentError) end it "cannot be blank" do expect { node.chef_environment("")}.to raise_error(Chef::Exceptions::ValidationFailed) end end describe "attributes" do it "should have attributes" do expect(node.attribute).to be_a_kind_of(Hash) end it "should allow attributes to be accessed by name or symbol directly on node[]" do node.default["locust"] = "something" expect(node[:locust]).to eql("something") expect(node["locust"]).to eql("something") end it "should return nil if it cannot find an attribute with node[]" do expect(node["secret"]).to eql(nil) end it "does not allow you to set an attribute via node[]=" do expect { node["secret"] = "shush" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end it "should allow you to query whether an attribute exists with attribute?" do node.default["locust"] = "something" expect(node.attribute?("locust")).to eql(true) expect(node.attribute?("no dice")).to eql(false) end it "should let you go deep with attribute?" do node.set["battles"]["people"]["wonkey"] = true expect(node["battles"]["people"].attribute?("wonkey")).to eq(true) expect(node["battles"]["people"].attribute?("snozzberry")).to eq(false) end it "does not allow you to set an attribute via method_missing" do expect { node.sunshine = "is bright"}.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end it "should allow you get get an attribute via method_missing" do node.default.sunshine = "is bright" expect(node.sunshine).to eql("is bright") end describe "normal attributes" do it "should allow you to set an attribute with set, without pre-declaring a hash" do node.set[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set an attribute with set_unless" do node.set_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(false) end it "should not allow you to set an attribute with set_unless if it already exists" do node.set[:snoopy][:is_a_puppy] = true node.set_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set a value after a set_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.set_unless[:snoopy][:is_a_puppy] = false node.set[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should let you set a value after a 'dangling' set_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.set[:snoopy][:is_a_puppy] = "what" node.set_unless[:snoopy][:is_a_puppy] node.set[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "auto-vivifies attributes created via method syntax" do node.set.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end it "should let you use tag as a convience method for the tags attribute" do node.normal['tags'] = ['one', 'two'] node.tag('three', 'four') expect(node['tags']).to eq(['one', 'two', 'three', 'four']) end end describe "default attributes" do it "should be set with default, without pre-declaring a hash" do node.default[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set with default_unless without pre-declaring a hash" do node.default_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(false) end it "should not allow you to set an attribute with default_unless if it already exists" do node.default[:snoopy][:is_a_puppy] = true node.default_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set a value after a default_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.default_unless[:snoopy][:is_a_puppy] = false node.default[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set a value after a 'dangling' default_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.default[:snoopy][:is_a_puppy] = "what" node.default_unless[:snoopy][:is_a_puppy] node.default[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "auto-vivifies attributes created via method syntax" do node.default.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end end describe "override attributes" do it "should be set with override, without pre-declaring a hash" do node.override[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set with override_unless without pre-declaring a hash" do node.override_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(false) end it "should not allow you to set an attribute with override_unless if it already exists" do node.override[:snoopy][:is_a_puppy] = true node.override_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set a value after an override_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.override_unless[:snoopy][:is_a_puppy] = false node.override[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set a value after a 'dangling' override_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 node.override_unless[:snoopy][:is_a_puppy] = "what" node.override_unless[:snoopy][:is_a_puppy] node.override[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "auto-vivifies attributes created via method syntax" do node.override.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end end describe "globally deleting attributes" do context "with hash values" do before do node.role_default["mysql"]["server"]["port"] = 1234 node.normal["mysql"]["server"]["port"] = 2345 node.override["mysql"]["server"]["port"] = 3456 end it "deletes all the values and returns the value with the highest precidence" do expect( node.rm("mysql", "server", "port") ).to eql(3456) expect( node["mysql"]["server"]["port"] ).to be_nil expect( node["mysql"]["server"] ).to eql({}) end it "deletes nested things correctly" do node.default["mysql"]["client"]["client_setting"] = "foo" expect( node.rm("mysql", "server") ).to eql( {"port" => 3456} ) expect( node["mysql"] ).to eql( { "client" => { "client_setting" => "foo" } } ) end it "returns nil if the node attribute does not exist" do expect( node.rm("no", "such", "thing") ).to be_nil end it "can delete the entire tree" do expect( node.rm("mysql") ).to eql({"server"=>{"port"=>3456}}) end end context "when trying to delete through a thing that isn't an array-like or hash-like object" do before do node.default["mysql"] = true end it "returns nil when you're two levels deeper" do expect( node.rm("mysql", "server", "port") ).to eql(nil) end it "returns nil when you're one level deeper" do expect( node.rm("mysql", "server") ).to eql(nil) end it "correctly deletes at the top level" do expect( node.rm("mysql") ).to eql(true) end end context "with array indexes" do before do node.role_default["mysql"]["server"][0]["port"] = 1234 node.normal["mysql"]["server"][0]["port"] = 2345 node.override["mysql"]["server"][0]["port"] = 3456 node.override["mysql"]["server"][1]["port"] = 3456 end it "deletes the array element" do expect( node.rm("mysql", "server", 0, "port") ).to eql(3456) expect( node["mysql"]["server"][0]["port"] ).to be_nil expect( node["mysql"]["server"][1]["port"] ).to eql(3456) end end context "with real arrays" do before do node.role_default["mysql"]["server"] = [ { "port" => 1234, } ] node.normal["mysql"]["server"] = [ { "port" => 2345, } ] node.override["mysql"]["server"] = [ { "port" => 3456, } ] end it "deletes the array element" do expect( node.rm("mysql", "server", 0, "port") ).to eql(3456) expect( node["mysql"]["server"][0]["port"] ).to be_nil end it "does not have a horrible error message when mistaking arrays for hashes" do expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)") end end end describe "granular deleting attributes" do context "when only defaults exist" do before do node.role_default["mysql"]["server"]["port"] = 1234 node.default["mysql"]["server"]["port"] = 2345 node.force_default["mysql"]["server"]["port"] = 3456 end it "returns the deleted values" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) end it "returns nil for the combined attribues" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) expect( node["mysql"]["server"]["port"] ).to eql(nil) end it "returns an empty hash for the default attrs" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) # this auto-vivifies, should it? expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({}) end it "returns an empty hash after the last key is deleted" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) expect( node["mysql"]["server"] ).to eql({}) end end context "when trying to delete through a thing that isn't an array-like or hash-like object" do before do node.default["mysql"] = true end it "returns nil when you're two levels deeper" do expect( node.rm_default("mysql", "server", "port") ).to eql(nil) end it "returns nil when you're one level deeper" do expect( node.rm_default("mysql", "server") ).to eql(nil) end it "correctly deletes at the top level" do expect( node.rm_default("mysql") ).to eql(true) end end context "when a higher precedence exists" do before do node.role_default["mysql"]["server"]["port"] = 1234 node.default["mysql"]["server"]["port"] = 2345 node.force_default["mysql"]["server"]["port"] = 3456 node.override["mysql"]["server"]["port"] = 9999 end it "returns the deleted values" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) end it "returns the higher precedence values after the delete" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) expect( node["mysql"]["server"]["port"] ).to eql(9999) end it "returns an empty has for the default attrs" do expect( node.rm_default("mysql", "server", "port") ).to eql(3456) # this auto-vivifies, should it? expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({}) end end context "when a lower precedence exists" do before do node.default["mysql"]["server"]["port"] = 2345 node.override["mysql"]["server"]["port"] = 9999 node.role_override["mysql"]["server"]["port"] = 9876 node.force_override["mysql"]["server"]["port"] = 6669 end it "returns the deleted values" do expect( node.rm_override("mysql", "server", "port") ).to eql(6669) end it "returns the lower precedence levels after the delete" do expect( node.rm_override("mysql", "server", "port") ).to eql(6669) expect( node["mysql"]["server"]["port"] ).to eql(2345) end it "returns an empty has for the override attrs" do expect( node.rm_override("mysql", "server", "port") ).to eql(6669) # this auto-vivifies, should it? expect( node.override_attrs["mysql"]["server"]["port"] ).to eql({}) end end it "rm_default returns nil on deleting non-existent values" do expect( node.rm_default("no", "such", "thing") ).to be_nil end it "rm_normal returns nil on deleting non-existent values" do expect( node.rm_normal("no", "such", "thing") ).to be_nil end it "rm_override returns nil on deleting non-existent values" do expect( node.rm_override("no", "such", "thing") ).to be_nil end end describe "granular replacing attributes" do it "removes everything at the level of the last key" do node.default["mysql"]["server"]["port"] = 2345 node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql" }) end it "replaces a value at the cookbook sub-level of the atributes only" do node.default["mysql"]["server"]["port"] = 2345 node.default["mysql"]["server"]["service_name"] = "fancypants-sql" node.role_default["mysql"]["server"]["port"] = 1234 node.force_default["mysql"]["server"]["port"] = 3456 node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } expect( node["mysql"]["server"]["port"] ).to eql(3456) expect( node["mysql"]["server"]["service_name"] ).to be_nil expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") expect( node["mysql"]["server"] ).to eql({ "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" }) end it "higher precedence values aren't removed" do node.role_default["mysql"]["server"]["port"] = 1234 node.default["mysql"]["server"]["port"] = 2345 node.force_default["mysql"]["server"]["port"] = 3456 node.override["mysql"]["server"]["service_name"] = "fancypants-sql" node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } expect( node["mysql"]["server"]["port"] ).to eql(3456) expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") expect( node["mysql"]["server"] ).to eql({ "service_name" => "fancypants-sql", "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" }) end end describe "granular force replacing attributes" do it "removes everything at the level of the last key" do node.force_default["mysql"]["server"]["port"] = 2345 node.force_default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql", }) end it "removes all values from the precedence level when setting" do node.role_default["mysql"]["server"]["port"] = 1234 node.default["mysql"]["server"]["port"] = 2345 node.force_default["mysql"]["server"]["port"] = 3456 node.force_default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"]["port"] ).to be_nil expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql", }) end it "higher precedence levels are not removed" do node.role_default["mysql"]["server"]["port"] = 1234 node.default["mysql"]["server"]["port"] = 2345 node.force_default["mysql"]["server"]["port"] = 3456 node.override["mysql"]["server"]["service_name"] = "fancypants-sql" node.force_default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"]["port"] ).to be_nil expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") expect( node["mysql"]["server"] ).to eql({ "service_name" => "fancypants-sql", "data_dir" => "/my_raid_volume/lib/mysql", }) end it "will autovivify" do node.force_default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") end it "lower precedence levels aren't removed" do node.role_override["mysql"]["server"]["port"] = 1234 node.override["mysql"]["server"]["port"] = 2345 node.force_override["mysql"]["server"]["port"] = 3456 node.default["mysql"]["server"]["service_name"] = "fancypants-sql" node.force_override!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"]["port"] ).to be_nil expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") expect( node["mysql"]["server"] ).to eql({ "service_name" => "fancypants-sql", "data_dir" => "/my_raid_volume/lib/mysql", }) end it "when overwriting a non-hash/array" do node.override["mysql"] = false node.force_override["mysql"] = true node.force_override!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") end it "when overwriting an array with a hash" do node.force_override["mysql"][0] = true node.force_override!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql", } expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql", }) end end # In Chef-12.0 there is a deep_merge cache on the top level attribute which had a bug # where it cached node[:foo] separate from node['foo']. These tests exercise those edge conditions. # # https://github.com/opscode/chef/issues/2700 # https://github.com/opscode/chef/issues/2712 # https://github.com/opscode/chef/issues/2745 # describe "deep merge attribute cache edge conditions" do it "does not error with complicated attribute substitution" do node.default['chef_attribute_hell']['attr1'] = "attribute1" node.default['chef_attribute_hell']['attr2'] = "#{node.chef_attribute_hell.attr1}/attr2" expect { node.default['chef_attribute_hell']['attr3'] = "#{node.chef_attribute_hell.attr2}/attr3" }.not_to raise_error end it "caches both strings and symbols correctly" do node.force_default[:solr][:version] = '4.10.2' node.force_default[:solr][:data_dir] = "/opt/solr-#{node['solr'][:version]}/example/solr" node.force_default[:solr][:xms] = "512M" expect(node[:solr][:xms]).to eql("512M") expect(node['solr'][:xms]).to eql("512M") end it "method interpolation syntax also works" do node.default['passenger']['version'] = '4.0.57' node.default['passenger']['root_path'] = "passenger-#{node['passenger']['version']}" node.default['passenger']['root_path_2'] = "passenger-#{node.passenger['version']}" expect(node['passenger']['root_path_2']).to eql("passenger-4.0.57") expect(node[:passenger]['root_path_2']).to eql("passenger-4.0.57") end end it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do expect { node.sunshine }.to raise_error(NoMethodError) end it "should allow you to iterate over attributes with each_attribute" do node.default.sunshine = "is bright" node.default.canada = "is a nice place" seen_attributes = Hash.new node.each_attribute do |a,v| seen_attributes[a] = v end expect(seen_attributes).to have_key("sunshine") expect(seen_attributes).to have_key("canada") expect(seen_attributes["sunshine"]).to eq("is bright") expect(seen_attributes["canada"]).to eq("is a nice place") end end describe "consuming json" do before do @ohai_data = {:platform => 'foo', :platform_version => 'bar'} end it "consumes the run list portion of a collection of attributes and returns the remainder" do attrs = {"run_list" => [ "role[base]", "recipe[chef::server]" ], "foo" => "bar"} expect(node.consume_run_list(attrs)).to eq({"foo" => "bar"}) expect(node.run_list).to eq([ "role[base]", "recipe[chef::server]" ]) end it "should overwrites the run list with the run list it consumes" do node.consume_run_list "recipes" => [ "one", "two" ] node.consume_run_list "recipes" => [ "three" ] expect(node.run_list).to eq([ "three" ]) end it "should not add duplicate recipes from the json attributes" do node.run_list << "one" node.consume_run_list "recipes" => [ "one", "two", "three" ] expect(node.run_list).to eq([ "one", "two", "three" ]) end it "doesn't change the run list if no run_list is specified in the json" do node.run_list << "role[database]" node.consume_run_list "foo" => "bar" expect(node.run_list).to eq(["role[database]"]) end it "raises an exception if you provide both recipe and run_list attributes, since this is ambiguous" do expect { node.consume_run_list "recipes" => "stuff", "run_list" => "other_stuff" }.to raise_error(Chef::Exceptions::AmbiguousRunlistSpecification) end it "should add json attributes to the node" do node.consume_external_attrs(@ohai_data, {"one" => "two", "three" => "four"}) expect(node.one).to eql("two") expect(node.three).to eql("four") end it "should set the tags attribute to an empty array if it is not already defined" do node.consume_external_attrs(@ohai_data, {}) expect(node.tags).to eql([]) end it "should not set the tags attribute to an empty array if it is already defined" do node.normal[:tags] = [ "radiohead" ] node.consume_external_attrs(@ohai_data, {}) expect(node.tags).to eql([ "radiohead" ]) end it "deep merges attributes instead of overwriting them" do node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}}) expect(node.one.to_hash).to eq({"two" => {"three" => "four"}}) node.consume_external_attrs(@ohai_data, "one" => {"abc" => "123"}) node.consume_external_attrs(@ohai_data, "one" => {"two" => {"foo" => "bar"}}) expect(node.one.to_hash).to eq({"two" => {"three" => "four", "foo" => "bar"}, "abc" => "123"}) end it "gives attributes from JSON priority when deep merging" do node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}}) expect(node.one.to_hash).to eq({"two" => {"three" => "four"}}) node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "forty-two"}}) expect(node.one.to_hash).to eq({"two" => {"three" => "forty-two"}}) end end describe "preparing for a chef client run" do before do @ohai_data = {:platform => 'foobuntu', :platform_version => '23.42'} end it "sets its platform according to platform detection" do node.consume_external_attrs(@ohai_data, {}) expect(node.automatic_attrs[:platform]).to eq('foobuntu') expect(node.automatic_attrs[:platform_version]).to eq('23.42') end it "consumes the run list from provided json attributes" do node.consume_external_attrs(@ohai_data, {"run_list" => ['recipe[unicorn]']}) expect(node.run_list).to eq(['recipe[unicorn]']) end it "saves non-runlist json attrs for later" do expansion = Chef::RunList::RunListExpansion.new('_default', []) allow(node.run_list).to receive(:expand).and_return(expansion) node.consume_external_attrs(@ohai_data, {"foo" => "bar"}) node.expand! expect(node.normal_attrs).to eq({"foo" => "bar", "tags" => []}) end end describe "when expanding its run list and merging attributes" do before do @environment = Chef::Environment.new.tap do |e| e.name('rspec_env') e.default_attributes("env default key" => "env default value") e.override_attributes("env override key" => "env override value") end expect(Chef::Environment).to receive(:load).with("rspec_env").and_return(@environment) @expansion = Chef::RunList::RunListExpansion.new("rspec_env", []) node.chef_environment("rspec_env") allow(node.run_list).to receive(:expand).and_return(@expansion) end it "sets the 'recipes' automatic attribute to the recipes in the expanded run_list" do @expansion.recipes << 'recipe[chef::client]' << 'recipe[nginx::default]' node.expand! expect(node.automatic_attrs[:recipes]).to eq(['recipe[chef::client]', 'recipe[nginx::default]']) end it "sets the 'roles' automatic attribute to the expanded role list" do @expansion.instance_variable_set(:@applied_roles, {'arf' => nil, 'countersnark' => nil}) node.expand! expect(node.automatic_attrs[:roles].sort).to eq(['arf', 'countersnark']) end it "applies default attributes from the environment as environment defaults" do node.expand! expect(node.attributes.env_default["env default key"]).to eq("env default value") end it "applies override attributes from the environment as env overrides" do node.expand! expect(node.attributes.env_override["env override key"]).to eq("env override value") end it "applies default attributes from roles as role defaults" do @expansion.default_attrs["role default key"] = "role default value" node.expand! expect(node.attributes.role_default["role default key"]).to eq("role default value") end it "applies override attributes from roles as role overrides" do @expansion.override_attrs["role override key"] = "role override value" node.expand! expect(node.attributes.role_override["role override key"]).to eq("role override value") end end describe "loaded_recipe" do it "should not add a recipe that is already in the recipes list" do node.automatic_attrs[:recipes] = [ "nginx::module" ] node.loaded_recipe(:nginx, "module") expect(node.automatic_attrs[:recipes].length).to eq(1) end it "should add a recipe that is not already in the recipes list" do node.automatic_attrs[:recipes] = [ "nginx::other_module" ] node.loaded_recipe(:nginx, "module") expect(node.automatic_attrs[:recipes].length).to eq(2) expect(node.recipe?("nginx::module")).to be true expect(node.recipe?("nginx::other_module")).to be true end end describe "when querying for recipes in the run list" do context "when a recipe is in the top level run list" do before do node.run_list << "recipe[nginx::module]" end it "finds the recipe" do expect(node.recipe?("nginx::module")).to be true end it "does not find a recipe not in the run list" do expect(node.recipe?("nginx::other_module")).to be false end end context "when a recipe is in the expanded run list only" do before do node.run_list << "role[base]" node.automatic_attrs[:recipes] = [ "nginx::module" ] end it "finds a recipe in the expanded run list" do expect(node.recipe?("nginx::module")).to be true end it "does not find a recipe that's not in the run list" do expect(node.recipe?("nginx::other_module")).to be false end end end describe "when clearing computed state at the beginning of a run" do before do node.default[:foo] = "default" node.normal[:foo] = "normal" node.override[:foo] = "override" node.reset_defaults_and_overrides end it "removes default attributes" do expect(node.default).to be_empty end it "removes override attributes" do expect(node.override).to be_empty end it "leaves normal level attributes untouched" do expect(node[:foo]).to eq("normal") end end describe "when merging environment attributes" do before do node.chef_environment = "rspec" @expansion = Chef::RunList::RunListExpansion.new("rspec", []) @expansion.default_attrs.replace({:default => "from role", :d_role => "role only"}) @expansion.override_attrs.replace({:override => "from role", :o_role => "role only"}) @environment = Chef::Environment.new @environment.default_attributes = {:default => "from env", :d_env => "env only" } @environment.override_attributes = {:override => "from env", :o_env => "env only"} allow(Chef::Environment).to receive(:load).and_return(@environment) node.apply_expansion_attributes(@expansion) end it "does not nuke role-only default attrs" do expect(node[:d_role]).to eq("role only") end it "does not nuke role-only override attrs" do expect(node[:o_role]).to eq("role only") end it "does not nuke env-only default attrs" do expect(node[:o_env]).to eq("env only") end it "does not nuke role-only override attrs" do expect(node[:o_env]).to eq("env only") end it "gives role defaults precedence over env defaults" do expect(node[:default]).to eq("from role") end it "gives env overrides precedence over role overrides" do expect(node[:override]).to eq("from env") end end describe "when evaluating attributes files" do before do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) @cookbook_loader = Chef::CookbookLoader.new(@cookbook_repo) @cookbook_loader.load_cookbooks @cookbook_collection = Chef::CookbookCollection.new(@cookbook_loader.cookbooks_by_name) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(node, @cookbook_collection, @events) node.include_attribute("openldap::default") node.include_attribute("openldap::smokey") end it "sets attributes from the files" do expect(node.ldap_server).to eql("ops1prod") expect(node.ldap_basedn).to eql("dc=hjksolutions,dc=com") expect(node.ldap_replication_password).to eql("forsure") expect(node.smokey).to eql("robinson") end it "gives a sensible error when attempting to load a missing attributes file" do expect { node.include_attribute("nope-this::doesnt-exist") }.to raise_error(Chef::Exceptions::CookbookNotFound) end end describe "roles" do it "should allow you to query whether or not it has a recipe applied with role?" do node.run_list << "role[sunrise]" expect(node.role?("sunrise")).to eql(true) expect(node.role?("not at home")).to eql(false) end it "should allow you to set roles with arguments" do node.run_list << "role[one]" node.run_list << "role[two]" expect(node.role?("one")).to eql(true) expect(node.role?("two")).to eql(true) end end describe "run_list" do it "should have a Chef::RunList of recipes and roles that should be applied" do expect(node.run_list).to be_a_kind_of(Chef::RunList) end it "should allow you to query the run list with arguments" do node.run_list "recipe[baz]" expect(node.run_list?("recipe[baz]")).to eql(true) end it "should allow you to set the run list with arguments" do node.run_list "recipe[baz]", "role[foo]" expect(node.run_list?("recipe[baz]")).to eql(true) expect(node.run_list?("role[foo]")).to eql(true) end end describe "from file" do it "should load a node from a ruby file" do node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb"))) expect(node.name).to eql("test.example.com-short") expect(node.sunshine).to eql("in") expect(node.something).to eql("else") expect(node.run_list).to eq(["operations-master", "operations-monitoring"]) end it "should raise an exception if the file cannot be found or read" do expect { node.from_file("/tmp/monkeydiving") }.to raise_error(IOError) end end describe "update_from!" do before(:each) do node.name("orig") node.chef_environment("dev") node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } } node.override_attrs = { "one" => { "two" => "three", "four" => "six" } } node.normal_attrs = { "one" => { "two" => "seven" } } node.run_list << "role[marxist]" node.run_list << "role[leninist]" node.run_list << "recipe[stalinist]" @example = Chef::Node.new() @example.name("newname") @example.chef_environment("prod") @example.default_attrs = { "alpha" => { "bravo" => "charlie", "delta" => "echo" } } @example.override_attrs = { "alpha" => { "bravo" => "foxtrot", "delta" => "golf" } } @example.normal_attrs = { "alpha" => { "bravo" => "hotel" } } @example.run_list << "role[comedy]" @example.run_list << "role[drama]" @example.run_list << "recipe[mystery]" end it "allows update of everything except name" do node.update_from!(@example) expect(node.name).to eq("orig") expect(node.chef_environment).to eq(@example.chef_environment) expect(node.default_attrs).to eq(@example.default_attrs) expect(node.override_attrs).to eq(@example.override_attrs) expect(node.normal_attrs).to eq(@example.normal_attrs) expect(node.run_list).to eq(@example.run_list) end it "should not update the name of the node" do expect(node).not_to receive(:name).with(@example.name) node.update_from!(@example) end end describe "to_hash" do it "should serialize itself as a hash" do node.chef_environment("dev") node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } } node.override_attrs = { "one" => { "two" => "three", "four" => "six" } } node.normal_attrs = { "one" => { "two" => "seven" } } node.run_list << "role[marxist]" node.run_list << "role[leninist]" node.run_list << "recipe[stalinist]" h = node.to_hash expect(h["one"]["two"]).to eq("three") expect(h["one"]["four"]).to eq("six") expect(h["one"]["eight"]).to eq("nine") expect(h["role"]).to be_include("marxist") expect(h["role"]).to be_include("leninist") expect(h["run_list"]).to be_include("role[marxist]") expect(h["run_list"]).to be_include("role[leninist]") expect(h["run_list"]).to be_include("recipe[stalinist]") expect(h["chef_environment"]).to eq("dev") end it 'should return an empty array for empty run_list' do expect(node.to_hash["run_list"]).to eq([]) end end describe "converting to or from json" do it "should serialize itself as json", :json => true do node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA)) json = Chef::JSONCompat.to_json(node) expect(json).to match(/json_class/) expect(json).to match(/name/) expect(json).to match(/chef_environment/) expect(json).to match(/normal/) expect(json).to match(/default/) expect(json).to match(/override/) expect(json).to match(/run_list/) end it 'should serialize valid json with a run list', :json => true do #This test came about because activesupport mucks with Chef json serialization #Test should pass with and without Activesupport node.run_list << {"type" => "role", "name" => 'Cthulu'} node.run_list << {"type" => "role", "name" => 'Hastur'} json = Chef::JSONCompat.to_json(node) expect(json).to match(/\"run_list\":\[\"role\[Cthulu\]\",\"role\[Hastur\]\"\]/) end it "should serialize the correct run list", :json => true do node.run_list << "role[marxist]" node.run_list << "role[leninist]" node.override_runlist << "role[stalinist]" expect(node.run_list).to be_include("role[stalinist]") json = Chef::JSONCompat.to_json(node) expect(json).to match(/\"run_list\":\[\"role\[marxist\]\",\"role\[leninist\]\"\]/) end it "merges the override components into a combined override object" do node.attributes.role_override["role override"] = "role override" node.attributes.env_override["env override"] = "env override" node_for_json = node.for_json expect(node_for_json["override"]["role override"]).to eq("role override") expect(node_for_json["override"]["env override"]).to eq("env override") end it "merges the default components into a combined default object" do node.attributes.role_default["role default"] = "role default" node.attributes.env_default["env default"] = "env default" node_for_json = node.for_json expect(node_for_json["default"]["role default"]).to eq("role default") expect(node_for_json["default"]["env default"]).to eq("env default") end it "should deserialize itself from json", :json => true do node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA)) json = Chef::JSONCompat.to_json(node) serialized_node = Chef::JSONCompat.from_json(json) expect(serialized_node).to be_a_kind_of(Chef::Node) expect(serialized_node.name).to eql(node.name) expect(serialized_node.chef_environment).to eql(node.chef_environment) node.each_attribute do |k,v| expect(serialized_node[k]).to eql(v) end expect(serialized_node.run_list).to eq(node.run_list) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA)) node } end end describe "to_s" do it "should turn into a string like node[name]" do node.name("airplane") expect(node.to_s).to eql("node[airplane]") end end describe "api model" do before(:each) do @rest = double("Chef::REST") allow(Chef::REST).to receive(:new).and_return(@rest) @query = double("Chef::Search::Query") allow(Chef::Search::Query).to receive(:new).and_return(@query) end describe "list" do describe "inflated" do it "should return a hash of node names and objects" do n1 = double("Chef::Node", :name => "one") expect(@query).to receive(:search).with(:node).and_yield(n1) r = Chef::Node.list(true) expect(r["one"]).to eq(n1) end end it "should return a hash of node names and urls" do expect(@rest).to receive(:get_rest).and_return({ "one" => "http://foo" }) r = Chef::Node.list expect(r["one"]).to eq("http://foo") end end describe "load" do it "should load a node by name" do expect(@rest).to receive(:get_rest).with("nodes/monkey").and_return("foo") expect(Chef::Node.load("monkey")).to eq("foo") end end describe "destroy" do it "should destroy a node" do expect(@rest).to receive(:delete_rest).with("nodes/monkey").and_return("foo") node.name("monkey") node.destroy end end describe "save" do it "should update a node if it already exists" do node.name("monkey") allow(node).to receive(:data_for_save).and_return({}) expect(@rest).to receive(:put_rest).with("nodes/monkey", {}).and_return("foo") node.save end it "should not try and create if it can update" do node.name("monkey") allow(node).to receive(:data_for_save).and_return({}) expect(@rest).to receive(:put_rest).with("nodes/monkey", {}).and_return("foo") expect(@rest).not_to receive(:post_rest) node.save end it "should create if it cannot update" do node.name("monkey") allow(node).to receive(:data_for_save).and_return({}) exception = double("404 error", :code => "404") expect(@rest).to receive(:put_rest).and_raise(Net::HTTPServerException.new("foo", exception)) expect(@rest).to receive(:post_rest).with("nodes", {}) node.save end describe "when whyrun mode is enabled" do before do Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end it "should not save" do node.name("monkey") expect(@rest).not_to receive(:put_rest) expect(@rest).not_to receive(:post_rest) node.save end end context "with whitelisted attributes configured" do it "should only save whitelisted attributes (and subattributes)" do Chef::Config[:automatic_attribute_whitelist] = [ ["filesystem", "/dev/disk0s2"], "network/interfaces/eth0" ] data = { "automatic" => { "filesystem" => { "/dev/disk0s2" => { "size" => "10mb" }, "map - autohome" => { "size" => "10mb" } }, "network" => { "interfaces" => { "eth0" => {}, "eth1" => {} } } }, "default" => {}, "normal" => {}, "override" => {} } selected_data = { "automatic" => { "filesystem" => { "/dev/disk0s2" => { "size" => "10mb" } }, "network" => { "interfaces" => { "eth0" => {} } } }, "default" => {}, "normal" => {}, "override" => {} } node.name("picky-monkey") allow(node).to receive(:for_json).and_return(data) expect(@rest).to receive(:put_rest).with("nodes/picky-monkey", selected_data).and_return("foo") node.save end it "should save false-y whitelisted attributes" do Chef::Config[:default_attribute_whitelist] = [ "foo/bar/baz" ] data = { "default" => { "foo" => { "bar" => { "baz" => false, }, "other" => { "stuff" => true, } } } } selected_data = { "default" => { "foo" => { "bar" => { "baz" => false, } } } } node.name("falsey-monkey") allow(node).to receive(:for_json).and_return(data) expect(@rest).to receive(:put_rest).with("nodes/falsey-monkey", selected_data).and_return("foo") node.save end it "should not save any attributes if the whitelist is empty" do Chef::Config[:automatic_attribute_whitelist] = [] data = { "automatic" => { "filesystem" => { "/dev/disk0s2" => { "size" => "10mb" }, "map - autohome" => { "size" => "10mb" } } }, "default" => {}, "normal" => {}, "override" => {} } selected_data = { "automatic" => {}, "default" => {}, "normal" => {}, "override" => {} } node.name("picky-monkey") allow(node).to receive(:for_json).and_return(data) expect(@rest).to receive(:put_rest).with("nodes/picky-monkey", selected_data).and_return("foo") node.save end end end end end chef-12.3.0/spec/unit/resource_collection/0000755000004100000410000000000012520074675020515 5ustar www-datawww-datachef-12.3.0/spec/unit/resource_collection/stepable_iterator_spec.rb0000644000004100000410000001006312520074675025564 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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::ResourceCollection::StepableIterator do CRSI = Chef::ResourceCollection::StepableIterator it "has an empty array for its collection by default" do expect(CRSI.new.collection).to eq([]) end describe "doing basic iteration" do before do @simple_collection = [1,2,3,4] @iterator = CRSI.for_collection(@simple_collection) end it "re-initializes the instance with a collection" do expect(@iterator.collection).to equal(@simple_collection) expect(@iterator.size).to eq(4) end it "iterates over the collection" do sum = 0 @iterator.each do |int| sum += int end expect(sum).to eq(10) end it "iterates over the collection with each_index" do collected_by_index = [] @iterator.each_index do |idx| collected_by_index << @simple_collection[idx] end expect(collected_by_index).to eq(@simple_collection) expect(collected_by_index).not_to equal(@simple_collection) end it "iterates over the collection with index and element" do collected = {} @iterator.each_with_index do |element, index| collected[index] = element end expect(collected).to eq({0=>1, 1=>2, 2=>3, 3=>4}) end end describe "pausing and resuming iteration" do before do @collection = [] @snitch_var = nil @collection << lambda { @snitch_var = 23 } @collection << lambda { @iterator.pause } @collection << lambda { @snitch_var = 42 } @iterator = CRSI.for_collection(@collection) @iterator.each { |proc| proc.call } end it "allows the iteration to be paused" do expect(@snitch_var).to eq(23) end it "allows the iteration to be resumed" do expect(@snitch_var).to eq(23) @iterator.resume expect(@snitch_var).to eq(42) end it "allows iteration to be rewound" do @iterator.skip_back(2) @iterator.resume expect(@snitch_var).to eq(23) @iterator.resume expect(@snitch_var).to eq(42) end it "allows iteration to be fast forwarded" do @iterator.skip_forward @iterator.resume expect(@snitch_var).to eq(23) end it "allows iteration to be rewound" do @snitch_var = nil @iterator.rewind expect(@iterator.position).to eq(0) @iterator.resume expect(@snitch_var).to eq(23) end it "allows iteration to be stepped" do @snitch_var = nil @iterator.rewind @iterator.step expect(@iterator.position).to eq(1) expect(@snitch_var).to eq(23) end it "doesn't step if there are no more steps" do expect(@iterator.step).to eq(3) expect {@iterator.step}.not_to raise_error expect(@iterator.step).to be_nil end it "allows the iteration to start by being stepped" do @snitch_var = nil @iterator = CRSI.for_collection(@collection) @iterator.iterate_on(:element) { |proc| proc.call } @iterator.step expect(@iterator.position).to eq(1) expect(@snitch_var).to eq(23) end it "should work correctly when elements are added to the collection during iteration" do @collection.insert(2, lambda { @snitch_var = 815}) @collection.insert(3, lambda { @iterator.pause }) @iterator.resume expect(@snitch_var).to eq(815) @iterator.resume expect(@snitch_var).to eq(42) end end end chef-12.3.0/spec/unit/resource_collection/resource_set_spec.rb0000644000004100000410000002004412520074675024556 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::ResourceCollection::ResourceSet do let(:collection) { Chef::ResourceCollection::ResourceSet.new } let(:zen_master_name) { "Neo" } let(:zen_master2_name) { "Morpheus" } let(:zen_follower_name) { "Squid" } let(:zen_master) { Chef::Resource::ZenMaster.new(zen_master_name) } let(:zen_master2) { Chef::Resource::ZenMaster.new(zen_master2_name) } let(:zen_follower) { Chef::Resource::ZenFollower.new(zen_follower_name) } describe "initialize" do it "should return a Chef::ResourceSet" do expect(collection).to be_instance_of(Chef::ResourceCollection::ResourceSet) end end describe "keys" do it "should return an empty list for an empty ResourceSet" do expect(collection.keys).to eq([]) end it "should return the keys for a non-empty ResourceSet" do collection.instance_variable_get(:@resources_by_key)["key"] = nil expect(collection.keys).to eq(["key"]) end end describe "insert_as, lookup and find" do # To validate insert_as you need lookup, and vice-versa - putting all tests in 1 context to avoid duplication it "should accept only Chef::Resources" do expect { collection.insert_as(zen_master) }.to_not raise_error expect { collection.insert_as("string") }.to raise_error(ArgumentError) end it "should allow you to lookup resources by a default .to_s" do collection.insert_as(zen_master) expect(collection.lookup(zen_master.to_s)).to equal(zen_master) end it "should use a custom type and name to insert" do collection.insert_as(zen_master, "OtherResource", "other_resource") expect(collection.lookup("OtherResource[other_resource]")).to equal(zen_master) end it "should raise an exception if you send something strange to lookup" do expect { collection.lookup(:symbol) }.to raise_error(ArgumentError) end it "should raise an exception if it cannot find a resource with lookup" do expect { collection.lookup(zen_master.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) end it "should find a resource by type symbol and name" do collection.insert_as(zen_master) expect(collection.find(:zen_master => zen_master_name)).to equal(zen_master) end it "should find a resource by type symbol and array of names" do collection.insert_as(zen_master) collection.insert_as(zen_master2) check_by_names(collection.find(:zen_master => [zen_master_name,zen_master2_name]), zen_master_name, zen_master2_name) end it "should find a resource by type symbol and array of names with custom names" do collection.insert_as(zen_master, :zzz, "name1") collection.insert_as(zen_master2, :zzz, "name2") check_by_names(collection.find( :zzz => ["name1","name2"]), zen_master_name, zen_master2_name) end it "should find resources of multiple kinds (:zen_master => a, :zen_follower => b)" do collection.insert_as(zen_master) collection.insert_as(zen_follower) check_by_names(collection.find(:zen_master => [zen_master_name], :zen_follower => [zen_follower_name]), zen_master_name, zen_follower_name) end it "should find resources of multiple kinds (:zen_master => a, :zen_follower => b with custom names)" do collection.insert_as(zen_master, :zzz, "name1") collection.insert_as(zen_master2, :zzz, "name2") collection.insert_as(zen_follower, :yyy, "name3") check_by_names(collection.find(:zzz => ["name1","name2"], :yyy => ["name3"]), zen_master_name, zen_follower_name, zen_master2_name) end it "should find a resource by string zen_master[a]" do collection.insert_as(zen_master) expect(collection.find("zen_master[#{zen_master_name}]")).to eq(zen_master) end it "should find a resource by string zen_master[a] with custom names" do collection.insert_as(zen_master, :zzz, "name1") expect(collection.find("zzz[name1]")).to eq(zen_master) end it "should find resources by strings of zen_master[a,b]" do collection.insert_as(zen_master) collection.insert_as(zen_master2) check_by_names(collection.find("zen_master[#{zen_master_name},#{zen_master2_name}]"), zen_master_name, zen_master2_name) end it "should find resources by strings of zen_master[a,b] with custom names" do collection.insert_as(zen_master, :zzz, "name1") collection.insert_as(zen_master2, :zzz, "name2") check_by_names(collection.find("zzz[name1,name2]"), zen_master_name, zen_master2_name) end it "should find resources of multiple types by strings of zen_master[a]" do collection.insert_as(zen_master) collection.insert_as(zen_follower) check_by_names(collection.find("zen_master[#{zen_master_name}]", "zen_follower[#{zen_follower_name}]"), zen_master_name, zen_follower_name) end it "should find resources of multiple types by strings of zen_master[a] with custom names" do collection.insert_as(zen_master, :zzz, "name1") collection.insert_as(zen_master2, :zzz, "name2") collection.insert_as(zen_follower, :yyy, "name3") check_by_names(collection.find("zzz[name1,name2]", "yyy[name3]"), zen_master_name, zen_follower_name,zen_master2_name) end it "should only keep the last copy when multiple instances of a Resource are inserted" do collection.insert_as(zen_master) expect(collection.find("zen_master[#{zen_master_name}]")).to eq(zen_master) new_zm =zen_master.dup new_zm.retries = 10 expect(new_zm).to_not eq(zen_master) collection.insert_as(new_zm) expect(collection.find("zen_master[#{zen_master_name}]")).to eq(new_zm) end it "should raise an exception if you pass a bad name to resources" do expect { collection.find("michael jackson") }.to raise_error(ArgumentError) end it "should raise an exception if you pass something other than a string or hash to resource" do expect { collection.find([Array.new]) }.to raise_error(ArgumentError) end it "raises an error when attempting to find a resource that does not exist" do expect { collection.find("script[nonesuch]") }.to raise_error(Chef::Exceptions::ResourceNotFound) end end describe "validate_lookup_spec!" do it "accepts a string of the form 'resource_type[resource_name]'" do expect(collection.validate_lookup_spec!("resource_type[resource_name]")).to be_truthy end it "accepts a single-element :resource_type => 'resource_name' Hash" do expect(collection.validate_lookup_spec!(:service => "apache2")).to be_truthy end it "accepts a chef resource object" do expect(collection.validate_lookup_spec!(zen_master)).to be_truthy end it "rejects a malformed query string" do expect { collection.validate_lookup_spec!("resource_type[missing-end-bracket") }.to \ raise_error(Chef::Exceptions::InvalidResourceSpecification) end it "rejects an argument that is not a String, Hash, or Chef::Resource" do expect { collection.validate_lookup_spec!(Object.new) }.to \ raise_error(Chef::Exceptions::InvalidResourceSpecification) end end def check_by_names(results, *names) expect(results.size).to eq(names.size) names.each do |name| expect(results.detect{|r| r.name == name}).to_not eq(nil) end end end chef-12.3.0/spec/unit/resource_collection/resource_list_spec.rb0000644000004100000410000001002312520074675024732 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::ResourceCollection::ResourceList do let(:resource_list) { Chef::ResourceCollection::ResourceList.new() } let(:resource) { Chef::Resource::ZenMaster.new("makoto") } let(:second_resource) { Chef::Resource::ZenMaster.new("hattori") } def insert_resource(res) expect{ resource_list.insert(res) }.not_to raise_error end describe "initialize" do it "should return a Chef::ResourceList" do expect(resource_list).to be_instance_of(Chef::ResourceCollection::ResourceList) end end describe "insert" do it "should be able to insert a Chef::Resource" do insert_resource(resource) expect(resource_list[0]).to be(resource) end it "should insert things in order" do insert_resource(resource) insert_resource(second_resource) expect(resource_list[0]).to be(resource) expect(resource_list[1]).to be(second_resource) end it "should raise error when trying to install something other than Chef::Resource" do expect{ resource_list.insert("not a resource") }.to raise_error(ArgumentError) end end describe "accessors" do it "should be able to insert with []=" do expect{ resource_list[0] = resource }.not_to raise_error expect{ resource_list[1] = second_resource }.not_to raise_error expect(resource_list[0]).to be(resource) expect(resource_list[1]).to be(second_resource) end it "should be empty by default" do expect(resource_list.empty?).to be_truthy end describe "when resources are inserted" do before do insert_resource(resource) insert_resource(second_resource) end it "should get resources with all_resources method" do resources = resource_list.all_resources expect(resources[0]).to be(resource) expect(resources[1]).to be(second_resource) end it "should be able to get resources with each" do current = 0 expected_resources = [resource, second_resource] resource_list.each do |r| expect(r).to be(expected_resources[current]) current += 1 end expect(current).to eq(2) end it "should be able to get resources with each_index" do current = 0 resource_list.each_index do |i| expect(i).to eq(current) current += 1 end expect(current).to eq(2) end it "should be able to check if the list is empty" do expect(resource_list.empty?).to be_falsey end end end describe "during execute" do before(:each) do insert_resource(resource) insert_resource(second_resource) end it "should execute resources in order" do current = 0 expected_resources = [resource, second_resource] resource_list.execute_each_resource do |r| expect(r).to be(expected_resources[current]) current += 1 end expect(current).to eq(2) end it "should be able to insert resources on the fly" do resource_to_inject = Chef::Resource::ZenMaster.new("there is no spoon") expected_resources = [resource, resource_to_inject, second_resource] resource_list.execute_each_resource do |r| resource_list.insert(resource_to_inject) if r == resource end expect(resource_list.all_resources).to eq(expected_resources) end end end chef-12.3.0/spec/unit/guard_interpreter/0000755000004100000410000000000012520074675020200 5ustar www-datawww-datachef-12.3.0/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb0000644000004100000410000001256612520074675027205 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::GuardInterpreter::ResourceGuardInterpreter do let(:node) do node = Chef::Node.new node.default["kernel"] = Hash.new node.default["kernel"][:machine] = :x86_64.to_s node end let(:run_context) { Chef::RunContext.new(node, nil, nil) } let(:parent_resource) do parent_resource = Chef::Resource.new("powershell_unit_test", run_context) allow(parent_resource).to receive(:run_action) allow(parent_resource).to receive(:updated).and_return(true) parent_resource end let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "echo hi", nil) } describe "get_interpreter_resource" do it "allows the guard interpreter to be set to Chef::Resource::Script" do parent_resource.guard_interpreter(:script) expect { guard_interpreter }.not_to raise_error end it "allows the guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do parent_resource.guard_interpreter(:powershell_script) expect { guard_interpreter }.not_to raise_error end it "raises an exception if guard_interpreter is set to a resource not derived from Chef::Resource::Script" do parent_resource.guard_interpreter(:file) expect { guard_interpreter }.to raise_error(ArgumentError, 'Specified guard interpreter class Chef::Resource::File must be a kind of Chef::Resource::Execute resource') end context "when the resource cannot be found for the platform" do before do expect(Chef::Resource).to receive(:resource_for_node).with(:foobar, node).and_return(nil) end it "raises an exception" do parent_resource.guard_interpreter(:foobar) expect { guard_interpreter }.to raise_error(ArgumentError, 'Specified guard_interpreter resource foobar unknown for this platform') end end it "fails when parent_resource is nil" do expect { Chef::GuardInterpreter::ResourceGuardInterpreter.new(nil, "echo hi", nil) }.to raise_error(ArgumentError, /Node for guard resource parent must not be nil/) end end describe "#evaluate" do let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "echo hi", {}) } let(:parent_resource) do parent_resource = Chef::Resource.new("execute resource", run_context) parent_resource.guard_interpreter(:execute) parent_resource end it "successfully evaluates the resource" do expect(guard_interpreter.evaluate).to eq(true) end describe "script command opts switch" do let(:command_opts) { {} } let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "exit 0", command_opts) } context "resource is a Script" do context "and guard_interpreter is a :script" do let(:parent_resource) do parent_resource = Chef::Resource::Script.new("resource", run_context) # Ruby scripts are cross platform to both Linux and Windows parent_resource.guard_interpreter(:ruby) parent_resource end let(:shell_out) { instance_double(Mixlib::ShellOut, :live_stream => true, :run_command => true, :error! => nil) } before do # TODO for some reason Windows is failing on executing a ruby script expect(Mixlib::ShellOut).to receive(:new) do |*args| expect(args[0]).to match(/^"ruby"/) shell_out end end it "merges to :code" do expect(command_opts).to receive(:merge).with({:code => "exit 0"}).and_call_original expect(guard_interpreter.evaluate).to eq(true) end end context "and guard_interpreter is :execute" do let(:parent_resource) do parent_resource = Chef::Resource::Script.new("resource", run_context) parent_resource.guard_interpreter(:execute) parent_resource end it "merges to :code" do expect(command_opts).to receive(:merge).with({:command => "exit 0"}).and_call_original expect(guard_interpreter.evaluate).to eq(true) end end end context "resource is not a Script" do let(:parent_resource) do parent_resource = Chef::Resource::Execute.new("resource", run_context) parent_resource.guard_interpreter(:execute) parent_resource end it "merges to :command" do expect(command_opts).to receive(:merge).with({:command => "exit 0"}).and_call_original expect(guard_interpreter.evaluate).to eq(true) end end end end end chef-12.3.0/spec/unit/rest/0000755000004100000410000000000012520074675015430 5ustar www-datawww-datachef-12.3.0/spec/unit/rest/auth_credentials_spec.rb0000644000004100000410000003224612520074675022314 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2010 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 'uri' require 'net/https' KEY_DOT_PEM=<<-END_RSA_KEY -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh 8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD 2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/ /RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4 DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8 OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12 2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ== -----END RSA PRIVATE KEY----- END_RSA_KEY describe Chef::REST::AuthCredentials do before do @key_file_fixture = CHEF_SPEC_DATA + '/ssl/private_key.pem' @key = OpenSSL::PKey::RSA.new(IO.read(@key_file_fixture).strip) @auth_credentials = Chef::REST::AuthCredentials.new("client-name", @key) end it "has a client name" do expect(@auth_credentials.client_name).to eq("client-name") end it "loads the private key when initialized with the path to the key" do expect(@auth_credentials.key).to respond_to(:private_encrypt) expect(@auth_credentials.key.to_s).to eq(KEY_DOT_PEM) end describe "when loading the private key" do it "strips extra whitespace before checking the key" do key_file_fixture = CHEF_SPEC_DATA + '/ssl/private_key_with_whitespace.pem' expect {Chef::REST::AuthCredentials.new("client-name", @key_file_fixture)}.not_to raise_error end end describe "generating signature headers for a request" do before do @request_time = Time.at(1270920860) @request_params = {:http_method => :POST, :path => "/clients", :body => '{"some":"json"}', :host => "localhost"} end it "generates signature headers for the request" do allow(Time).to receive(:now).and_return(@request_time) actual = @auth_credentials.signature_headers(@request_params) expect(actual["HOST"]).to eq("localhost") expect(actual["X-OPS-AUTHORIZATION-1"]).to eq("kBssX1ENEwKtNYFrHElN9vYGWS7OeowepN9EsYc9csWfh8oUovryPKDxytQ/") expect(actual["X-OPS-AUTHORIZATION-2"]).to eq("Wc2/nSSyxdWJjjfHzrE+YrqNQTaArOA7JkAf5p75eTUonCWcvNPjFrZVgKGS") expect(actual["X-OPS-AUTHORIZATION-3"]).to eq("yhzHJQh+lcVA9wwARg5Hu9q+ddS8xBOdm3Vp5atl5NGHiP0loiigMYvAvzPO") expect(actual["X-OPS-AUTHORIZATION-4"]).to eq("r9853eIxwYMhn5hLGhAGFQznJbE8+7F/lLU5Zmk2t2MlPY8q3o1Q61YD8QiJ") expect(actual["X-OPS-AUTHORIZATION-5"]).to eq("M8lIt53ckMyUmSU0DDURoiXLVkE9mag/6Yq2tPNzWq2AdFvBqku9h2w+DY5k") expect(actual["X-OPS-AUTHORIZATION-6"]).to eq("qA5Rnzw5rPpp3nrWA9jKkPw4Wq3+4ufO2Xs6w7GCjA==") expect(actual["X-OPS-CONTENT-HASH"]).to eq("1tuzs5XKztM1ANrkGNPah6rW9GY=") expect(actual["X-OPS-SIGN"]).to match(%r{(version=1\.0)|(algorithm=sha1;version=1.0;)}) expect(actual["X-OPS-TIMESTAMP"]).to eq("2010-04-10T17:34:20Z") expect(actual["X-OPS-USERID"]).to eq("client-name") end describe "when configured for version 1.1 of the authn protocol" do before do Chef::Config[:authentication_protocol_version] = "1.1" end after do Chef::Config[:authentication_protocol_version] = "1.0" end it "generates the correct signature for version 1.1" do allow(Time).to receive(:now).and_return(@request_time) actual = @auth_credentials.signature_headers(@request_params) expect(actual["HOST"]).to eq("localhost") expect(actual["X-OPS-CONTENT-HASH"]).to eq("1tuzs5XKztM1ANrkGNPah6rW9GY=") expect(actual["X-OPS-SIGN"]).to eq("algorithm=sha1;version=1.1;") expect(actual["X-OPS-TIMESTAMP"]).to eq("2010-04-10T17:34:20Z") expect(actual["X-OPS-USERID"]).to eq("client-name") # mixlib-authN will test the actual signature stuff for each version of # the protocol so we won't test it again here. end end end end describe Chef::REST::RESTRequest do def new_request(method=nil) method ||= :POST Chef::REST::RESTRequest.new(method, @url, @req_body, @headers) end before do @auth_credentials = Chef::REST::AuthCredentials.new("client-name", CHEF_SPEC_DATA + '/ssl/private_key.pem') @url = URI.parse("http://chef.example.com:4000/?q=chef_is_awesome") @req_body = '{"json_data":"as_a_string"}' @headers = { "Content-type" =>"application/json", "Accept"=>"application/json", "Accept-Encoding" => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, "Host" => "chef.example.com:4000" } @request = Chef::REST::RESTRequest.new(:POST, @url, @req_body, @headers) end it "stores the url it was created with" do expect(@request.url).to eq(@url) end it "stores the HTTP method" do expect(@request.method).to eq(:POST) end it "adds the chef version header" do expect(@request.headers).to eq(@headers.merge("X-Chef-Version" => ::Chef::VERSION)) end describe "configuring the HTTP request" do it "configures GET requests" do @req_body = nil rest_req = new_request(:GET) expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Get) expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome") expect(rest_req.http_request.body).to be_nil end it "configures POST requests, including the body" do expect(@request.http_request).to be_a_kind_of(Net::HTTP::Post) expect(@request.http_request.path).to eq("/?q=chef_is_awesome") expect(@request.http_request.body).to eq(@req_body) end it "configures PUT requests, including the body" do rest_req = new_request(:PUT) expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Put) expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome") expect(rest_req.http_request.body).to eq(@req_body) end it "configures DELETE requests" do rest_req = new_request(:DELETE) expect(rest_req.http_request).to be_a_kind_of(Net::HTTP::Delete) expect(rest_req.http_request.path).to eq("/?q=chef_is_awesome") expect(rest_req.http_request.body).to be_nil end it "configures HTTP basic auth" do @url = URI.parse("http://homie:theclown@chef.example.com:4000/?q=chef_is_awesome") rest_req = new_request(:GET) expect(rest_req.http_request.to_hash["authorization"]).to eq(["Basic aG9taWU6dGhlY2xvd24="]) end end describe "configuring the HTTP client" do it "configures the HTTP client for the host and port" do http_client = new_request.http_client expect(http_client.address).to eq("chef.example.com") expect(http_client.port).to eq(4000) end it "configures the HTTP client with the read timeout set in the config file" do Chef::Config[:rest_timeout] = 9001 expect(new_request.http_client.read_timeout).to eq(9001) end describe "for proxy" do before do Chef::Config[:http_proxy] = "http://proxy.example.com:3128" Chef::Config[:https_proxy] = "http://sproxy.example.com:3129" Chef::Config[:http_proxy_user] = nil Chef::Config[:http_proxy_pass] = nil Chef::Config[:https_proxy_user] = nil Chef::Config[:https_proxy_pass] = nil Chef::Config[:no_proxy] = nil end after do Chef::Config[:http_proxy] = nil Chef::Config[:https_proxy] = nil Chef::Config[:http_proxy_user] = nil Chef::Config[:http_proxy_pass] = nil Chef::Config[:https_proxy_user] = nil Chef::Config[:https_proxy_pass] = nil Chef::Config[:no_proxy] = nil end describe "with :no_proxy nil" do it "configures the proxy address and port when using http scheme" do http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_address).to eq("proxy.example.com") expect(http_client.proxy_port).to eq(3128) expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end it "configures the proxy address and port when using https scheme" do @url.scheme = "https" http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_address).to eq("sproxy.example.com") expect(http_client.proxy_port).to eq(3129) expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end end describe "with :no_proxy set" do before do Chef::Config[:no_proxy] = "10.*,*.example.com" end it "does not configure the proxy address and port when using http scheme" do http_client = new_request.http_client expect(http_client.proxy?).to eq(false) expect(http_client.proxy_address).to be_nil expect(http_client.proxy_port).to be_nil expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end it "does not configure the proxy address and port when using https scheme" do @url.scheme = "https" http_client = new_request.http_client expect(http_client.proxy?).to eq(false) expect(http_client.proxy_address).to be_nil expect(http_client.proxy_port).to be_nil expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end end describe "with :http_proxy_user and :http_proxy_pass set" do before do Chef::Config[:http_proxy_user] = "homie" Chef::Config[:http_proxy_pass] = "theclown" end after do Chef::Config[:http_proxy_user] = nil Chef::Config[:http_proxy_pass] = nil end it "configures the proxy user and pass when using http scheme" do http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_user).to eq("homie") expect(http_client.proxy_pass).to eq("theclown") end it "does not configure the proxy user and pass when using https scheme" do @url.scheme = "https" http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end end describe "with :https_proxy_user and :https_proxy_pass set" do before do Chef::Config[:https_proxy_user] = "homie" Chef::Config[:https_proxy_pass] = "theclown" end after do Chef::Config[:https_proxy_user] = nil Chef::Config[:https_proxy_pass] = nil end it "does not configure the proxy user and pass when using http scheme" do http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_user).to be_nil expect(http_client.proxy_pass).to be_nil end it "configures the proxy user and pass when using https scheme" do @url.scheme = "https" http_client = new_request.http_client expect(http_client.proxy?).to eq(true) expect(http_client.proxy_user).to eq("homie") expect(http_client.proxy_pass).to eq("theclown") end end end end end chef-12.3.0/spec/unit/application/0000755000004100000410000000000012520074675016756 5ustar www-datawww-datachef-12.3.0/spec/unit/application/server_spec.rb0000644000004100000410000000000012520074675021611 0ustar www-datawww-datachef-12.3.0/spec/unit/application/solo_spec.rb0000644000004100000410000001464112520074675021277 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Application::Solo do let(:app) { Chef::Application::Solo.new } before do allow(Kernel).to receive(:trap).and_return(:ok) allow(app).to receive(:configure_opt_parser).and_return(true) allow(app).to receive(:configure_chef).and_return(true) allow(app).to receive(:configure_logging).and_return(true) allow(app).to receive(:trap) Chef::Config[:recipe_url] = false Chef::Config[:json_attribs] = false Chef::Config[:solo] = true end describe "configuring the application" do it 'should call set_specific_recipes' do expect(app).to receive(:set_specific_recipes) app.reconfigure end it "should set solo mode to true" do app.reconfigure expect(Chef::Config[:solo]).to be_truthy end it "should set audit-mode to :disabled" do app.reconfigure expect(Chef::Config[:audit_mode]).to be :disabled end describe "when configured to not fork the client process" do before do Chef::Config[:client_fork] = false Chef::Config[:daemonize] = false Chef::Config[:interval] = nil Chef::Config[:splay] = nil end context "when interval is given" do before do Chef::Config[:interval] = 600 end it "should terminate with message" do expect(Chef::Application).to receive(:fatal!).with( "Unforked chef-client interval runs are disabled in Chef 12. Configuration settings: interval = 600 seconds Enable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options." ) app.reconfigure end end end describe "when in daemonized mode and no interval has been set" do before do Chef::Config[:daemonize] = true end it "should set the interval to 1800" do Chef::Config[:interval] = nil app.reconfigure expect(Chef::Config[:interval]).to eq(1800) end end describe "when the json_attribs configuration option is specified" do let(:json_attribs) { {"a" => "b"} } let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) } let(:json_source) { "https://foo.com/foo.json" } before do Chef::Config[:json_attribs] = json_source expect(Chef::ConfigFetcher).to receive(:new).with(json_source). and_return(config_fetcher) end it "reads the JSON attributes from the specified source" do app.reconfigure expect(app.chef_client_json).to eq(json_attribs) end end describe "when the recipe_url configuration option is specified" do let(:tarfile) { StringIO.new("remote_tarball_content") } let(:target_file) { StringIO.new } before do Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz" allow(FileUtils).to receive(:rm_rf).and_return(true) allow(FileUtils).to receive(:mkdir_p).and_return(true) allow(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile) allow(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file) allow(Chef::Mixin::Command).to receive(:run_command).and_return(true) end it "should create the recipes path based on the parent of the cookbook path" do expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) app.reconfigure end it "should download the recipes" do expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile) app.reconfigure end it "should write the recipes to the target path" do app.reconfigure expect(target_file.string).to eq("remote_tarball_content") end it "should untar the target file to the parent of the cookbook path" do expect(Chef::Mixin::Command).to receive(:run_command).with({:command => "tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo"}).and_return(true) app.reconfigure end end end describe "when the json_attribs and recipe_url configuration options are both specified" do let(:json_attribs) { {"a" => "b"} } let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) } let(:json_source) { "https://foo.com/foo.json" } before do Chef::Config[:json_attribs] = json_source Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats" Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" allow(FileUtils).to receive(:rm_rf).and_return(true) allow(FileUtils).to receive(:mkdir_p).and_return(true) allow(Chef::Mixin::Command).to receive(:run_command).and_return(true) end it "should fetch the recipe_url first" do expect(app).to receive(:fetch_recipe_tarball).ordered expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher) app.reconfigure end end describe "after the application has been configured" do before do Chef::Config[:solo] = true allow(Chef::Daemon).to receive(:change_privilege) chef_client = double("Chef::Client") allow(Chef::Client).to receive(:new).and_return(chef_client) # this is all stuff the reconfigure method needs allow(app).to receive(:configure_opt_parser).and_return(true) allow(app).to receive(:configure_chef).and_return(true) allow(app).to receive(:configure_logging).and_return(true) end it "should change privileges" do expect(Chef::Daemon).to receive(:change_privilege).and_return(true) app.setup_application end end end chef-12.3.0/spec/unit/application/knife_spec.rb0000644000004100000410000001345412520074675021420 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' require "#{CHEF_SPEC_DATA}/knife_subcommand/test_yourself" describe Chef::Application::Knife do include SpecHelpers::Knife before(:all) do class NoopKnifeCommand < Chef::Knife option :opt_with_default, :short => "-D VALUE", :long => "-optwithdefault VALUE", :default => "default-value" def run end end end after(:each) do # reset some really nasty global state NoopKnifeCommand.reset_config_loader! end before(:each) do # Prevent code from getting loaded on every test invocation. allow(Chef::Knife).to receive(:load_commands) @knife = Chef::Application::Knife.new allow(@knife).to receive(:puts) allow(@knife).to receive(:trap) allow(Chef::Knife).to receive(:list_commands) end it "should exit 1 and print the options if no arguments are given at all" do with_argv([]) do expect { @knife.run }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) } end end it "should exit 2 if run without a sub command" do with_argv("--user", "adam") do expect(Chef::Log).to receive(:error).with(/you need to pass a sub\-command/i) expect { @knife.run }.to raise_error(SystemExit) { |e| expect(e.status).to eq(2) } end end it "should run a sub command with the applications command line option prototype" do with_argv(*%w{noop knife command with some args}) do knife = double(Chef::Knife) expect(Chef::Knife).to receive(:run).with(ARGV, @knife.options).and_return(knife) expect(@knife).to receive(:exit).with(0) @knife.run end end it "should set the colored output to false by default on windows and true otherwise" do with_argv(*%w{noop knife command}) do expect(@knife).to receive(:exit).with(0) @knife.run end if windows? expect(Chef::Config[:color]).to be_falsey else expect(Chef::Config[:color]).to be_truthy end end describe "when given a path to the client key" do it "expands a relative path relative to the CWD" do relative_path = '.chef/client.pem' allow(Dir).to receive(:pwd).and_return(CHEF_SPEC_DATA) with_argv(*%W{noop knife command -k #{relative_path}}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:client_key]).to eq(File.join(CHEF_SPEC_DATA, relative_path)) end it "expands a ~/home/path to the correct full path" do home_path = '~/.chef/client.pem' with_argv(*%W{noop knife command -k #{home_path}}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:client_key]).to eq(File.join(ENV['HOME'], '.chef/client.pem').gsub((File::ALT_SEPARATOR || '\\'), File::SEPARATOR)) end it "does not expand a full path" do full_path = if windows? 'C:/chef/client.pem' else '/etc/chef/client.pem' end with_argv(*%W{noop knife command -k #{full_path}}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:client_key]).to eq(full_path) end end describe "with environment configuration" do before do Chef::Config[:environment] = nil end it "should default to no environment" do with_argv(*%w{noop knife command}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:environment]).to eq(nil) end it "should load the environment from the config file" do config_file = File.join(CHEF_SPEC_DATA,"environment-config.rb") with_argv(*%W{noop knife command -c #{config_file}}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:environment]).to eq('production') end it "should load the environment from the CLI options" do with_argv(*%W{noop knife command -E development}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:environment]).to eq('development') end it "should override the config file environment with the CLI environment" do config_file = File.join(CHEF_SPEC_DATA,"environment-config.rb") with_argv(*%W{noop knife command -c #{config_file} -E override}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:environment]).to eq('override') end it "should override the config file environment with the CLI environment regardless of order" do config_file = File.join(CHEF_SPEC_DATA,"environment-config.rb") with_argv(*%W{noop knife command -E override -c #{config_file}}) do expect(@knife).to receive(:exit).with(0) @knife.run end expect(Chef::Config[:environment]).to eq('override') end it "should run a sub command with the applications command line option prototype" do with_argv(*%w{noop knife command with some args}) do knife = double(Chef::Knife) expect(Chef::Knife).to receive(:run).with(ARGV, @knife.options).and_return(knife) expect(@knife).to receive(:exit).with(0) @knife.run end end end end chef-12.3.0/spec/unit/application/apply_spec.rb0000644000004100000410000000747212520074675021454 0ustar www-datawww-data# # Author:: Bryan W. Berry () # Copyright:: Copyright (c) 2012 Bryan W. Berry # 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::Application::Apply do before do @app = Chef::Application::Apply.new allow(@app).to receive(:configure_logging).and_return(true) @recipe_text = "package 'nyancat'" Chef::Config[:solo] = true end describe "configuring the application" do it "should set solo mode to true" do @app.reconfigure expect(Chef::Config[:solo]).to be_truthy end end describe "read_recipe_file" do before do @recipe_file_name = "foo.rb" @recipe_path = File.expand_path(@recipe_file_name) @recipe_file = double("Tempfile (mock)", :read => @recipe_text) allow(@app).to receive(:open).with(@recipe_path).and_return(@recipe_file) allow(File).to receive(:exist?).with(@recipe_path).and_return(true) allow(Chef::Application).to receive(:fatal!).and_return(true) end it "should read text properly" do expect(@app.read_recipe_file(@recipe_file_name)[0]).to eq(@recipe_text) end it "should return a file_handle" do expect(@app.read_recipe_file(@recipe_file_name)[1]).to be_instance_of(RSpec::Mocks::Double) end describe "when recipe is nil" do it "should raise a fatal with the missing filename message" do expect(Chef::Application).to receive(:fatal!).with("No recipe file was provided", 1) @app.read_recipe_file(nil) end end describe "when recipe doesn't exist" do before do allow(File).to receive(:exist?).with(@recipe_path).and_return(false) end it "should raise a fatal with the file doesn't exist message" do expect(Chef::Application).to receive(:fatal!).with(/^No file exists at/, 1) @app.read_recipe_file(@recipe_file_name) end end end describe "temp_recipe_file" do before do @app.instance_variable_set(:@recipe_text, @recipe_text) @app.temp_recipe_file @recipe_fh = @app.instance_variable_get(:@recipe_fh) end it "should open a tempfile" do expect(@recipe_fh.path).to match(/.*recipe-temporary-file.*/) end it "should write recipe text to the tempfile" do expect(@recipe_fh.read).to eq(@recipe_text) end it "should save the filename for later use" do expect(@recipe_fh.path).to eq(@app.instance_variable_get(:@recipe_filename)) end end describe "recipe_file_arg" do before do ARGV.clear end it "should exit and log message" do expect(Chef::Log).to receive(:debug).with(/^No recipe file provided/) expect { @app.run }.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) } end end describe "when the json_attribs configuration option is specified" do let(:json_attribs) { {"a" => "b"} } let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) } let(:json_source) { "https://foo.com/foo.json" } before do Chef::Config[:json_attribs] = json_source expect(Chef::ConfigFetcher).to receive(:new).with(json_source). and_return(config_fetcher) end it "reads the JSON attributes from the specified source" do @app.reconfigure expect(@app.json_attribs).to eq(json_attribs) end end end chef-12.3.0/spec/unit/application/client_spec.rb0000644000004100000410000002713112520074675021577 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Application::Client, "reconfigure" do let(:app) do a = described_class.new a.cli_arguments = [] a end before do allow(Kernel).to receive(:trap).and_return(:ok) allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("") @original_argv = ARGV.dup ARGV.clear allow(app).to receive(:trap) allow(app).to receive(:configure_logging).and_return(true) Chef::Config[:interval] = 10 Chef::Config[:once] = false end after do ARGV.replace(@original_argv) end describe 'parse cli_arguments' do it 'should call set_specific_recipes' do expect(app).to receive(:set_specific_recipes).and_return(true) app.reconfigure end end describe "when configured to not fork the client process" do before do Chef::Config[:client_fork] = false Chef::Config[:daemonize] = false Chef::Config[:interval] = nil Chef::Config[:splay] = nil end context "when interval is given" do before do Chef::Config[:interval] = 600 allow(Chef::Platform).to receive(:windows?).and_return(false) end it "should terminate with message" do expect(Chef::Application).to receive(:fatal!).with( "Unforked chef-client interval runs are disabled in Chef 12. Configuration settings: interval = 600 seconds Enable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options." ) app.reconfigure end end context "when interval is given on windows" do before do Chef::Config[:interval] = 600 allow(Chef::Platform).to receive(:windows?).and_return(true) end it "should not terminate" do expect(Chef::Application).not_to receive(:fatal!) app.reconfigure end end context "when configured to run once" do before do Chef::Config[:once] = true Chef::Config[:interval] = 1000 end it "should reconfigure chef-client" do app.reconfigure expect(Chef::Config[:interval]).to be_nil end end end describe "when in daemonized mode and no interval has been set" do before do Chef::Config[:daemonize] = true Chef::Config[:interval] = nil end it "should set the interval to 1800" do app.reconfigure expect(Chef::Config.interval).to eq(1800) end end describe "when configured to run once" do before do Chef::Config[:once] = true Chef::Config[:daemonize] = false Chef::Config[:splay] = 60 Chef::Config[:interval] = 1800 end it "ignores the splay" do app.reconfigure expect(Chef::Config.splay).to be_nil end it "forces the interval to nil" do app.reconfigure expect(Chef::Config.interval).to be_nil end end describe "when --no-listen is set" do it "configures listen = false" do app.config[:listen] = false app.reconfigure expect(Chef::Config[:listen]).to eq(false) end end describe "when the json_attribs configuration option is specified" do let(:json_attribs) { {"a" => "b"} } let(:config_fetcher) { double(Chef::ConfigFetcher, :fetch_json => json_attribs) } let(:json_source) { "https://foo.com/foo.json" } before do allow(app).to receive(:configure_chef).and_return(true) Chef::Config[:json_attribs] = json_source expect(Chef::ConfigFetcher).to receive(:new).with(json_source). and_return(config_fetcher) end it "reads the JSON attributes from the specified source" do app.reconfigure expect(app.chef_client_json).to eq(json_attribs) end end describe "audit mode" do shared_examples "experimental feature" do before do allow(Chef::Log).to receive(:warn) end it "emits a warning that audit mode is an experimental feature" do expect(Chef::Log).to receive(:warn).with(/Audit mode is an experimental feature/) app.reconfigure end end shared_examples "unrecognized setting" do it "fatals with a message including the incorrect setting" do expect(Chef::Application).to receive(:fatal!).with(/Unrecognized setting #{mode} for audit mode/) app.reconfigure end end shared_context "set via config file" do before do Chef::Config[:audit_mode] = mode end end shared_context "set via command line" do before do ARGV.replace(["--audit-mode", mode]) end end describe "enabled via config file" do include_context "set via config file" do let(:mode) { :enabled } include_examples "experimental feature" end end describe "enabled via command line" do include_context "set via command line" do let(:mode) { "enabled" } include_examples "experimental feature" end end describe "audit_only via config file" do include_context "set via config file" do let(:mode) { :audit_only } include_examples "experimental feature" end end describe "audit-only via command line" do include_context "set via command line" do let(:mode) { "audit-only" } include_examples "experimental feature" end end describe "unrecognized setting via config file" do include_context "set via config file" do let(:mode) { :derp } include_examples "unrecognized setting" end end describe "unrecognized setting via command line" do include_context "set via command line" do let(:mode) { "derp" } include_examples "unrecognized setting" end end end describe "when both the pidfile and lockfile opts are set to the same value" do before do Chef::Config[:pid_file] = "/path/to/file" Chef::Config[:lockfile] = "/path/to/file" end it "should throw an exception" do expect { @app.reconfigure }.to raise_error end end end describe Chef::Application::Client, "setup_application" do before do @app = Chef::Application::Client.new # this is all stuff the reconfigure method needs allow(@app).to receive(:configure_opt_parser).and_return(true) allow(@app).to receive(:configure_chef).and_return(true) allow(@app).to receive(:configure_logging).and_return(true) end it "should change privileges" do expect(Chef::Daemon).to receive(:change_privilege).and_return(true) @app.setup_application end after do Chef::Config[:solo] = false end end describe Chef::Application::Client, "configure_chef" do let(:app) { Chef::Application::Client.new } before do @original_argv = ARGV.dup ARGV.clear allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("") app.configure_chef end after do ARGV.replace(@original_argv) end it "should set the colored output to false by default on windows and true otherwise" do if windows? expect(Chef::Config[:color]).to be_falsey else expect(Chef::Config[:color]).to be_truthy end end end describe Chef::Application::Client, "run_application", :unix_only do before(:each) do Chef::Config[:specific_recipes] = [] # normally gets set in @app.reconfigure @app = Chef::Application::Client.new @app.setup_signal_handlers # Default logger doesn't work correctly when logging from a trap handler. @app.configure_logging @pipe = IO.pipe @client = Chef::Client.new allow(Chef::Client).to receive(:new).and_return(@client) allow(@client).to receive(:run) do @pipe[1].puts 'started' sleep 1 @pipe[1].puts 'finished' end end context "when sent SIGTERM", :volatile_on_solaris do context "when converging in forked process" do before do Chef::Config[:daemonize] = true allow(Chef::Daemon).to receive(:daemonize).and_return(true) end it "should exit hard with exitstatus 3", :volatile do pid = fork do @app.run_application end Process.kill("TERM", pid) _pid, result = Process.waitpid2(pid) expect(result.exitstatus).to eq(3) end it "should allow child to finish converging" do pid = fork do @app.run_application end expect(@pipe[0].gets).to eq("started\n") Process.kill("TERM", pid) Process.wait(pid) # The timeout value needs to be large enough for the child process to finish expect(IO.select([@pipe[0]], nil, nil, 15)).not_to be_nil expect(@pipe[0].gets).to eq("finished\n") end end context "when running unforked" do before(:each) do Chef::Config[:client_fork] = false Chef::Config[:daemonize] = false end it "should exit gracefully when sent during converge" do pid = fork do @app.run_application end expect(@pipe[0].gets).to eq("started\n") Process.kill("TERM", pid) _pid, result = Process.waitpid2(pid) expect(result.exitstatus).to eq(0) expect(IO.select([@pipe[0]], nil, nil, 0)).not_to be_nil expect(@pipe[0].gets).to eq("finished\n") end it "should exit hard when sent before converge" do pid = fork do sleep 3 @app.run_application end Process.kill("TERM", pid) _pid, result = Process.waitpid2(pid) expect(result.exitstatus).to eq(3) end end end describe "when splay is set" do before do Chef::Config[:splay] = 10 Chef::Config[:interval] = 10 run_count = 0 # uncomment to debug failures... # Chef::Log.init($stderr) # Chef::Log.level = :debug allow(@app).to receive(:run_chef_client) do run_count += 1 if run_count > 3 exit 0 end # If everything is fine, sending USR1 to self should prevent # app to go into splay sleep forever. Process.kill("USR1", Process.pid) # On Ruby < 2.1, we need to give the signal handlers a little # more time, otherwise the test will fail because interleavings. sleep 1 end number_of_sleep_calls = 0 # This is a very complicated way of writing # @app.should_receive(:sleep).once. # We have to do it this way because the main loop of # Chef::Application::Client swallows most exceptions, and we need to be # able to expose our expectation failures to the parent process in the test. allow(@app).to receive(:interval_sleep) do |arg| number_of_sleep_calls += 1 if number_of_sleep_calls > 1 exit 127 end end end it "shouldn't sleep when sent USR1" do allow(@app).to receive(:interval_sleep).with(0).and_call_original pid = fork do @app.run_application end _pid, result = Process.waitpid2(pid) expect(result.exitstatus).to eq(0) end end end chef-12.3.0/spec/unit/application/agent_spec.rb0000644000004100000410000000000012520074675021401 0ustar www-datawww-datachef-12.3.0/spec/unit/windows_service_spec.rb0000644000004100000410000000642012520074675021226 0ustar www-datawww-data# # Author:: Mukta Aphale () # 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' if Chef::Platform.windows? require 'chef/application/windows_service' end describe "Chef::Application::WindowsService", :windows_only do let (:instance) {Chef::Application::WindowsService.new} let (:shell_out_result) {Object.new} let (:tempfile) {Tempfile.new "log_file"} before do allow(instance).to receive(:parse_options) allow(shell_out_result).to receive(:stdout) allow(shell_out_result).to receive(:stderr) end it "runs chef-client in new process" do expect(instance).to receive(:configure_chef).twice instance.service_init expect(instance).to receive(:run_chef_client).and_call_original expect(instance).to receive(:shell_out).and_return(shell_out_result) allow(instance).to receive(:running?).and_return(true, false) allow(instance.instance_variable_get(:@service_signal)).to receive(:wait) allow(instance).to receive(:state).and_return(4) instance.service_main end context 'when running chef-client' do it "passes config params to new process with a default timeout of 2 hours (7200 seconds)" do Chef::Config.merge!({:log_location => tempfile.path, :config_file => "test_config_file", :log_level => :info}) expect(instance).to receive(:configure_chef).twice instance.service_init allow(instance).to receive(:running?).and_return(true, false) allow(instance.instance_variable_get(:@service_signal)).to receive(:wait) allow(instance).to receive(:state).and_return(4) expect(instance).to receive(:run_chef_client).and_call_original expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}", {:timeout => 7200}).and_return(shell_out_result) instance.service_main tempfile.unlink end it "passes config params to new process with a the timeout specified in the config" do Chef::Config.merge!({:log_location => tempfile.path, :config_file => "test_config_file", :log_level => :info}) Chef::Config[:windows_service][:watchdog_timeout] = 10 expect(instance).to receive(:configure_chef).twice instance.service_init allow(instance).to receive(:running?).and_return(true, false) allow(instance.instance_variable_get(:@service_signal)).to receive(:wait) allow(instance).to receive(:state).and_return(4) expect(instance).to receive(:run_chef_client).and_call_original expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}", {:timeout => 10}).and_return(shell_out_result) instance.service_main tempfile.unlink end end end chef-12.3.0/spec/unit/monkey_patches/0000755000004100000410000000000012520074675017464 5ustar www-datawww-datachef-12.3.0/spec/unit/monkey_patches/uri_spec.rb0000644000004100000410000000175212520074675021627 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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 URI do describe "when a URI contains an IPv6 literal" do let(:ipv6_uri) do URI.parse("https://[2a00:1450:4009:809::1008]:8443") end it "returns the hostname without brackets" do expect(ipv6_uri.hostname).to eq("2a00:1450:4009:809::1008") end end end chef-12.3.0/spec/unit/run_list_spec.rb0000644000004100000410000002474412520074675017664 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008-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 'spec_helper' require 'chef/version_class' require 'chef/version_constraint' describe Chef::RunList do before(:each) do @run_list = Chef::RunList.new end describe "<<" do it "should add a recipe to the run list and recipe list with the fully qualified name" do @run_list << 'recipe[needy]' expect(@run_list).to include('recipe[needy]') expect(@run_list.recipes).to include("needy") end it "should add a role to the run list and role list with the fully qualified name" do @run_list << "role[woot]" expect(@run_list).to include('role[woot]') expect(@run_list.roles).to include('woot') end it "should accept recipes that are unqualified" do @run_list << "needy" expect(@run_list).to include('recipe[needy]') expect(@run_list.recipes.include?('needy')).to eq(true) end it "should not allow duplicates" do @run_list << "needy" @run_list << "needy" expect(@run_list.run_list.length).to eq(1) expect(@run_list.recipes.length).to eq(1) end it "should allow two versions of a recipe" do @run_list << "recipe[needy@0.2.0]" @run_list << "recipe[needy@0.1.0]" expect(@run_list.run_list.length).to eq(2) expect(@run_list.recipes.length).to eq(2) expect(@run_list.recipes.include?('needy')).to eq(true) end it "should not allow duplicate versions of a recipe" do @run_list << "recipe[needy@0.2.0]" @run_list << "recipe[needy@0.2.0]" expect(@run_list.run_list.length).to eq(1) expect(@run_list.recipes.length).to eq(1) end end describe "add" do # Testing only the basic functionality here # since full behavior is tested above. it "should add a recipe to the run_list" do @run_list.add 'recipe[needy]' expect(@run_list).to include('recipe[needy]') end it "should add a role to the run_list" do @run_list.add 'role[needy]' expect(@run_list).to include('role[needy]') end end describe "==" do it "should believe two RunLists are equal if they have the same members" do @run_list << "foo" r = Chef::RunList.new r << "foo" expect(@run_list).to eq(r) end it "should believe a RunList is equal to an array named after it's members" do @run_list << "foo" @run_list << "baz" expect(@run_list).to eq([ "foo", "baz" ]) end end describe "empty?" do it "should be emtpy if the run list has no members" do expect(@run_list.empty?).to eq(true) end it "should not be empty if the run list has members" do @run_list << "chromeo" expect(@run_list.empty?).to eq(false) end end describe "[]" do it "should let you look up a member in the run list by position" do @run_list << 'recipe[loulou]' expect(@run_list[0]).to eq('recipe[loulou]') end end describe "[]=" do it "should let you set a member of the run list by position" do @run_list[0] = 'recipe[loulou]' expect(@run_list[0]).to eq('recipe[loulou]') end it "should properly expand a member of the run list given by position" do @run_list[0] = 'loulou' expect(@run_list[0]).to eq('recipe[loulou]') end end describe "each" do it "should yield each member to your block" do @run_list << "foo" @run_list << "bar" seen = Array.new @run_list.each { |r| seen << r } expect(seen).to be_include("recipe[foo]") expect(seen).to be_include("recipe[bar]") end end describe "each_index" do it "should yield each members index to your block" do to_add = [ "recipe[foo]", "recipe[bar]", "recipe[baz]" ] to_add.each { |i| @run_list << i } @run_list.each_index { |i| expect(@run_list[i]).to eq(to_add[i]) } end end describe "include?" do it "should be true if the run list includes the item" do @run_list << "foo" @run_list.include?("foo") end end describe "reset" do it "should reset the run_list based on the array you pass" do @run_list << "chromeo" list = %w{camp chairs snakes clowns} @run_list.reset!(list) list.each { |i| expect(@run_list).to be_include(i) } expect(@run_list.include?("chromeo")).to eq(false) end end describe "when expanding the run list" do before(:each) do @role = Chef::Role.new @role.name "stubby" @role.run_list "one", "two" @role.default_attributes :one => :two @role.override_attributes :three => :four allow(Chef::Role).to receive(:load).and_return(@role) @rest = double("Chef::REST", { :get_rest => @role, :url => "/" }) allow(Chef::REST).to receive(:new).and_return(@rest) @run_list << "role[stubby]" @run_list << "kitty" end describe "from disk" do it "should load the role from disk" do expect(Chef::Role).to receive(:from_disk).with("stubby") @run_list.expand("_default", "disk") end it "should log a helpful error if the role is not available" do allow(Chef::Role).to receive(:from_disk).and_raise(Chef::Exceptions::RoleNotFound) expect(Chef::Log).to receive(:error).with("Role stubby (included by 'top level') is in the runlist but does not exist. Skipping expand.") @run_list.expand("_default", "disk") end end describe "from the chef server" do it "should load the role from the chef server" do #@rest.should_receive(:get_rest).with("roles/stubby") expansion = @run_list.expand("_default", "server") expect(expansion.recipes).to eq(['one', 'two', 'kitty']) end it "should default to expanding from the server" do expect(@rest).to receive(:get_rest).with("roles/stubby") @run_list.expand("_default") end describe "with an environment set" do before do @role.env_run_list["production"] = Chef::RunList.new( "one", "two", "five") end it "expands the run list using the environment specific run list" do expansion = @run_list.expand("production", "server") expect(expansion.recipes).to eq(%w{one two five kitty}) end describe "and multiply nested roles" do before do @multiple_rest_requests = double("Chef::REST") @role.env_run_list["production"] << "role[prod-base]" @role_prod_base = Chef::Role.new @role_prod_base.name("prod-base") @role_prod_base.env_run_list["production"] = Chef::RunList.new("role[nested-deeper]") @role_nested_deeper = Chef::Role.new @role_nested_deeper.name("nested-deeper") @role_nested_deeper.env_run_list["production"] = Chef::RunList.new("recipe[prod-secret-sauce]") end it "expands the run list using the specified environment for all nested roles" do allow(Chef::REST).to receive(:new).and_return(@multiple_rest_requests) expect(@multiple_rest_requests).to receive(:get_rest).with("roles/stubby").and_return(@role) expect(@multiple_rest_requests).to receive(:get_rest).with("roles/prod-base").and_return(@role_prod_base) expect(@multiple_rest_requests).to receive(:get_rest).with("roles/nested-deeper").and_return(@role_nested_deeper) expansion = @run_list.expand("production", "server") expect(expansion.recipes).to eq(%w{one two five prod-secret-sauce kitty}) end end end end it "should return the list of expanded recipes" do expansion = @run_list.expand("_default") expect(expansion.recipes[0]).to eq("one") expect(expansion.recipes[1]).to eq("two") end it "should return the list of default attributes" do expansion = @run_list.expand("_default") expect(expansion.default_attrs[:one]).to eq(:two) end it "should return the list of override attributes" do expansion = @run_list.expand("_default") expect(expansion.override_attrs[:three]).to eq(:four) end it "should recurse into a child role" do dog = Chef::Role.new dog.name "dog" dog.default_attributes :seven => :nine dog.run_list "three" @role.run_list << "role[dog]" allow(Chef::Role).to receive(:from_disk).with("stubby").and_return(@role) allow(Chef::Role).to receive(:from_disk).with("dog").and_return(dog) expansion = @run_list.expand("_default", 'disk') expect(expansion.recipes[2]).to eq("three") expect(expansion.default_attrs[:seven]).to eq(:nine) end it "should not recurse infinitely" do dog = Chef::Role.new dog.name "dog" dog.default_attributes :seven => :nine dog.run_list "role[dog]", "three" @role.run_list << "role[dog]" allow(Chef::Role).to receive(:from_disk).with("stubby").and_return(@role) expect(Chef::Role).to receive(:from_disk).with("dog").once.and_return(dog) expansion = @run_list.expand("_default", 'disk') expect(expansion.recipes[2]).to eq("three") expect(expansion.recipes[3]).to eq("kitty") expect(expansion.default_attrs[:seven]).to eq(:nine) end end describe "when converting to an alternate representation" do before do @run_list << "recipe[nagios::client]" << "role[production]" << "recipe[apache2]" end it "converts to an array of the string forms of its items" do expect(@run_list.to_a).to eq(["recipe[nagios::client]", "role[production]", "recipe[apache2]"]) end it "converts to json by converting its array form" do expect(Chef::JSONCompat.to_json(@run_list)).to eq(Chef::JSONCompat.to_json(["recipe[nagios::client]", "role[production]", "recipe[apache2]"])) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @run_list } end end end chef-12.3.0/spec/unit/resource_builder_spec.rb0000644000004100000410000000003712520074675021347 0ustar www-datawww-data# see spec/unit/recipe_spec.rb chef-12.3.0/spec/unit/mash_spec.rb0000644000004100000410000000312012520074675016736 0ustar www-datawww-data# # Author:: Matthew Kent () # 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 'spec_helper' require 'chef/mash' describe Mash do it "should duplicate a simple key/value mash to a new mash" do data = {:x=>"one", :y=>"two", :z=>"three"} @orig = Mash.new(data) @copy = @orig.dup expect(@copy.to_hash).to eq(Mash.new(data).to_hash) @copy[:x] = "four" expect(@orig[:x]).to eq("one") end it "should duplicate a mash with an array to a new mash" do data = {:x=>"one", :y=>"two", :z=>[1,2,3]} @orig = Mash.new(data) @copy = @orig.dup expect(@copy.to_hash).to eq(Mash.new(data).to_hash) @copy[:z] << 4 expect(@orig[:z]).to eq([1,2,3]) end it "should duplicate a nested mash to a new mash" do data = {:x=>"one", :y=>"two", :z=>Mash.new({:a=>[1,2,3]})} @orig = Mash.new(data) @copy = @orig.dup expect(@copy.to_hash).to eq(Mash.new(data).to_hash) @copy[:z][:a] << 4 expect(@orig[:z][:a]).to eq([1,2,3]) end # add more! end chef-12.3.0/spec/unit/lwrp_spec.rb0000644000004100000410000004066412520074675017010 0ustar www-datawww-data# # Author:: Christopher Walters () # 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 'spec_helper' module LwrpConstScopingConflict end describe "LWRP" do before do @original_VERBOSE = $VERBOSE $VERBOSE = nil end after do $VERBOSE = @original_VERBOSE end describe "when overriding an existing class" do before :each do allow($stderr).to receive(:write) end it "should not skip loading a resource when there's a top level symbol of the same name" do Object.const_set('LwrpFoo', Class.new) file = File.expand_path( "lwrp/resources/foo.rb", CHEF_SPEC_DATA) expect(Chef::Log).not_to receive(:info).with(/Skipping/) expect(Chef::Log).not_to receive(:debug).with(/anymore/) Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) Object.send(:remove_const, 'LwrpFoo') Chef::Resource.send(:remove_const, 'LwrpFoo') end it "should not skip loading a provider when there's a top level symbol of the same name" do Object.const_set('LwrpBuckPasser', Class.new) file = File.expand_path( "lwrp/providers/buck_passer.rb", CHEF_SPEC_DATA) expect(Chef::Log).not_to receive(:info).with(/Skipping/) expect(Chef::Log).not_to receive(:debug).with(/anymore/) Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil) Object.send(:remove_const, 'LwrpBuckPasser') Chef::Provider.send(:remove_const, 'LwrpBuckPasser') end # @todo: we need a before block to manually remove_const all of the LWRPs that we # load in these tests. we're threading state through these tests in LWRPs that # have already been loaded in prior tests, which probably renders some of them bogus it "should log if attempting to load resource of same name" do Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file| expect(Chef::Log).to receive(:info).with(/Skipping/) expect(Chef::Log).to receive(:debug).with(/anymore/) Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end end it "should log if attempting to load provider of same name" do Dir[File.expand_path( "lwrp/providers/*", CHEF_SPEC_DATA)].each do |file| Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil) end Dir[File.expand_path( "lwrp/providers/*", CHEF_SPEC_DATA)].each do |file| expect(Chef::Log).to receive(:info).with(/Skipping/) expect(Chef::Log).to receive(:debug).with(/anymore/) Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil) end end it "keeps the old LRWP resource class in the list of resource subclasses" do # This was originally CHEF-3432 regression test. But with Chef 12 we are # not replacing the original classes anymore. Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end first_lwr_foo_class = Chef::Resource::LwrpFoo expect(Chef::Resource.resource_classes).to include(first_lwr_foo_class) Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end expect(Chef::Resource.resource_classes).to include(first_lwr_foo_class) end it "does not attempt to remove classes from higher up namespaces [CHEF-4117]" do conflicting_lwrp_file = File.expand_path( "lwrp_const_scoping/resources/conflict.rb", CHEF_SPEC_DATA) # The test is that this should not raise an error: Chef::Resource::LWRPBase.build_from_file("lwrp_const_scoping", conflicting_lwrp_file, nil) end end describe "Lightweight Chef::Resource" do before do Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil) end end it "should load the resource into a properly-named class" do expect(Chef::Resource.const_get("LwrpFoo")).to be_kind_of(Class) end it "should set resource_name" do expect(Chef::Resource::LwrpFoo.new("blah").resource_name).to eql(:lwrp_foo) end it "should add the specified actions to the allowed_actions array" do expect(Chef::Resource::LwrpFoo.new("blah").allowed_actions).to include(:pass_buck, :twiddle_thumbs) end it "should set the specified action as the default action" do expect(Chef::Resource::LwrpFoo.new("blah").action).to eq(:pass_buck) end it "should create a method for each attribute" do expect(Chef::Resource::LwrpFoo.new("blah").methods.map{ |m| m.to_sym}).to include(:monkey) end it "should build attribute methods that respect validation rules" do expect { Chef::Resource::LwrpFoo.new("blah").monkey(42) }.to raise_error(ArgumentError) end it "should have access to the run context and node during class definition" do node = Chef::Node.new node.normal[:penguin_name] = "jackass" run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new, @events) Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources_with_default_attributes", "*"))].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, run_context) end cls = Chef::Resource.const_get("LwrpNodeattr") expect(cls.node).to be_kind_of(Chef::Node) expect(cls.run_context).to be_kind_of(Chef::RunContext) expect(cls.node[:penguin_name]).to eql("jackass") end context "resource_name" do let(:klass) { Class.new(Chef::Resource::LWRPBase) } it "returns nil when the resource_name is not set" do expect(klass.resource_name).to be_nil end it "allows to user to user the resource_name" do expect { klass.resource_name(:foo) }.to_not raise_error end it "returns the set value for the resource" do klass.resource_name(:foo) expect(klass.resource_name).to eq(:foo) end context "when creating a new instance" do it "raises an exception if resource_name is nil" do expect { klass.new('blah') }.to raise_error(Chef::Exceptions::InvalidResourceSpecification) end end context "lazy default values" do let(:klass) do Class.new(Chef::Resource::LWRPBase) do self.resource_name = :sample_resource attribute :food, :default => lazy { 'BACON!'*3 } attribute :drink, :default => lazy { |r| "Drink after #{r.food}!"} end end let(:instance) { klass.new('kitchen') } it "evaluates the default value when requested" do expect(instance.food).to eq('BACON!BACON!BACON!') end it "evaluates yields self to the block" do expect(instance.drink).to eq('Drink after BACON!BACON!BACON!!') end end end describe "when #default_action is an array" do let(:lwrp) do Class.new(Chef::Resource::LWRPBase) do actions :eat, :sleep default_action [:eat, :sleep] end end it "returns the array of default actions" do expect(lwrp.default_action).to eq([:eat, :sleep]) end end describe "when inheriting from LWRPBase" do let(:parent) do Class.new(Chef::Resource::LWRPBase) do actions :eat, :sleep default_action :eat end end context "when the child does not defined the methods" do let(:child) do Class.new(parent) end it "delegates #actions to the parent" do expect(child.actions).to eq([:eat, :sleep]) end it "delegates #default_action to the parent" do expect(child.default_action).to eq(:eat) end end context "when the child does define the methods" do let(:child) do Class.new(parent) do actions :dont_eat, :dont_sleep default_action :dont_eat end end it "does not delegate #actions to the parent" do expect(child.actions).to eq([:dont_eat, :dont_sleep]) end it "does not delegate #default_action to the parent" do expect(child.default_action).to eq(:dont_eat) end end context "when actions are already defined" do let(:child) do Class.new(parent) do actions :eat actions :sleep actions :drink end end def raise_if_deprecated! if Chef::VERSION.split('.').first.to_i > 12 raise "This test should be removed and the associated code should be removed!" end end it "amends actions when they are already defined" do raise_if_deprecated! expect(child.actions).to eq([:eat, :sleep, :drink]) end end end end describe "Lightweight Chef::Provider" do before do @node = Chef::Node.new @node.automatic[:platform] = :ubuntu @node.automatic[:platform_version] = '8.10' @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new({}), @events) @runner = Chef::Runner.new(@run_context) end before(:each) do Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context) end Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file| Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context) end Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file| Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context) end Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "providers", "*"))].each do |file| Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context) end end it "should properly handle a new_resource reference" do resource = Chef::Resource::LwrpFoo.new("morpheus") resource.monkey("bob") resource.provider(:lwrp_monkey_name_printer) resource.run_context = @run_context provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs) provider.action_twiddle_thumbs end it "should load the provider into a properly-named class" do expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class) end it "should create a method for each attribute" do new_resource = double("new resource").as_null_object expect(Chef::Provider::LwrpBuckPasser.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_pass_buck) expect(Chef::Provider::LwrpThumbTwiddler.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_twiddle_thumbs) end it "should insert resources embedded in the provider into the middle of the resource collection" do injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context) injector.action(:pass_buck) injector.provider(:lwrp_buck_passer) dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context) dummy.provider(Chef::Provider::Easy) @run_context.resource_collection.insert(injector) @run_context.resource_collection.insert(dummy) Chef::Runner.new(@run_context).converge expect(@run_context.resource_collection[0]).to eql(injector) expect(@run_context.resource_collection[1].name).to eql('prepared_thumbs') expect(@run_context.resource_collection[2].name).to eql('twiddled_thumbs') expect(@run_context.resource_collection[3]).to eql(dummy) end it "should insert embedded resources from multiple providers, including from the last position, properly into the resource collection" do injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context) injector.action(:pass_buck) injector.provider(:lwrp_buck_passer) injector2 = Chef::Resource::LwrpBar.new("tank", @run_context) injector2.action(:pass_buck) injector2.provider(:lwrp_buck_passer_2) dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context) dummy.provider(Chef::Provider::Easy) @run_context.resource_collection.insert(injector) @run_context.resource_collection.insert(dummy) @run_context.resource_collection.insert(injector2) Chef::Runner.new(@run_context).converge expect(@run_context.resource_collection[0]).to eql(injector) expect(@run_context.resource_collection[1].name).to eql('prepared_thumbs') expect(@run_context.resource_collection[2].name).to eql('twiddled_thumbs') expect(@run_context.resource_collection[3]).to eql(dummy) expect(@run_context.resource_collection[4]).to eql(injector2) expect(@run_context.resource_collection[5].name).to eql('prepared_eyes') expect(@run_context.resource_collection[6].name).to eql('dried_paint_watched') end it "should properly handle a new_resource reference" do resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context) resource.monkey("bob") resource.provider(:lwrp_monkey_name_printer) provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs) provider.action_twiddle_thumbs expect(provider.monkey_name).to eq("my monkey's name is 'bob'") end it "should properly handle an embedded Resource accessing the enclosing Provider's scope" do resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context) resource.monkey("bob") resource.provider(:lwrp_embedded_resource_accesses_providers_scope) provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs) #provider = @runner.build_provider(resource) provider.action_twiddle_thumbs expect(provider.enclosed_resource.monkey).to eq('bob, the monkey') end describe "when using inline compilation" do before do # Behavior in these examples depends on implementation of fixture provider. # See spec/data/lwrp/providers/inline_compiler # Side effect of lwrp_inline_compiler provider for testing notifications. $interior_ruby_block_2 = nil # resource type doesn't matter, so make an existing resource type work with provider. @resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context) @resource.allowed_actions << :test @resource.action(:test) @resource.provider(:lwrp_inline_compiler) end it "does not add interior resources to the exterior resource collection" do @resource.run_action(:test) expect(@run_context.resource_collection).to be_empty end context "when interior resources are updated" do it "processes notifications within the LWRP provider's action" do @resource.run_action(:test) expect($interior_ruby_block_2).to eq("executed") end it "marks the parent resource updated" do @resource.run_action(:test) expect(@resource).to be_updated expect(@resource).to be_updated_by_last_action end end context "when interior resources are not updated" do it "does not mark the parent resource updated" do @resource.run_action(:no_updates) expect(@resource).not_to be_updated expect(@resource).not_to be_updated_by_last_action end end end end end chef-12.3.0/spec/unit/knife_spec.rb0000644000004100000410000004616612520074675017123 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2008, 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. # # Fixtures for subcommand loading live in this namespace module KnifeSpecs end require 'spec_helper' require 'uri' describe Chef::Knife do let(:stderr) { StringIO.new } let(:knife) { Chef::Knife.new } before(:each) do Chef::Log.logger = Logger.new(StringIO.new) Chef::Config[:node_name] = "webmonkey.example.com" # Prevent gratuitous code reloading: allow(Chef::Knife).to receive(:load_commands) allow(knife.ui).to receive(:puts) allow(knife.ui).to receive(:print) allow(Chef::Log).to receive(:init) allow(Chef::Log).to receive(:level) [:debug, :info, :warn, :error, :crit].each do |level_sym| allow(Chef::Log).to receive(level_sym) end allow(Chef::Knife).to receive(:puts) end after(:each) do Chef::Knife.reset_config_loader! end describe "after loading a subcommand" do before do Chef::Knife.reset_subcommands! if KnifeSpecs.const_defined?(:TestNameMapping) KnifeSpecs.send(:remove_const, :TestNameMapping) end if KnifeSpecs.const_defined?(:TestExplicitCategory) KnifeSpecs.send(:remove_const, :TestExplicitCategory) end Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_name_mapping.rb')) Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_explicit_category.rb')) end it "has a category based on its name" do expect(KnifeSpecs::TestNameMapping.subcommand_category).to eq('test') end it "has an explicitly defined category if set" do expect(KnifeSpecs::TestExplicitCategory.subcommand_category).to eq('cookbook site') end it "can reference the subcommand by its snake cased name" do expect(Chef::Knife.subcommands['test_name_mapping']).to equal(KnifeSpecs::TestNameMapping) end it "lists subcommands by category" do expect(Chef::Knife.subcommands_by_category['test']).to include('test_name_mapping') end it "lists subcommands by category when the subcommands have explicit categories" do expect(Chef::Knife.subcommands_by_category['cookbook site']).to include('test_explicit_category') end it "has empty dependency_loader list by default" do expect(KnifeSpecs::TestNameMapping.dependency_loaders).to be_empty end end describe "after loading all subcommands" do before do Chef::Knife.reset_subcommands! Chef::Knife.load_commands end it "references a subcommand class by its snake cased name" do class SuperAwesomeCommand < Chef::Knife end Chef::Knife.load_commands expect(Chef::Knife.subcommands).to have_key("super_awesome_command") expect(Chef::Knife.subcommands["super_awesome_command"]).to eq(SuperAwesomeCommand) end it "guesses a category from a given ARGV" do Chef::Knife.subcommands_by_category["cookbook"] << :cookbook Chef::Knife.subcommands_by_category["cookbook site"] << :cookbook_site expect(Chef::Knife.guess_category(%w{cookbook foo bar baz})).to eq('cookbook') expect(Chef::Knife.guess_category(%w{cookbook site foo bar baz})).to eq('cookbook site') expect(Chef::Knife.guess_category(%w{cookbook site --help})).to eq('cookbook site') end it "finds a subcommand class based on ARGV" do Chef::Knife.subcommands["cookbook_site_vendor"] = :CookbookSiteVendor Chef::Knife.subcommands["cookbook"] = :Cookbook expect(Chef::Knife.subcommand_class_from(%w{cookbook site vendor --help foo bar baz})).to eq(:CookbookSiteVendor) end end describe "the headers include X-Remote-Request-Id" do let(:headers) {{"Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", 'X-Chef-Version' => Chef::VERSION, "Host"=>"api.opscode.piab", "X-REMOTE-REQUEST-ID"=>request_id}} let(:request_id) {"1234"} let(:request_mock) { {} } let(:rest) do allow(Net::HTTP).to receive(:new).and_return(http_client) allow(Chef::RequestID.instance).to receive(:request_id).and_return(request_id) allow(Chef::Config).to receive(:chef_server_url).and_return("https://api.opscode.piab") command = Chef::Knife.run(%w{test yourself}) rest = command.noauth_rest rest end let!(:http_client) do http_client = Net::HTTP.new(url.host, url.port) allow(http_client).to receive(:request).and_yield(http_response).and_return(http_response) http_client end let(:url) { URI.parse("https://api.opscode.piab") } let(:http_response) do http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req") allow(http_response).to receive(:read_body) allow(http_response).to receive(:body).and_return(body) http_response["Content-Length"] = body.bytesize.to_s http_response end let(:body) { "ninja" } before(:each) do Chef::Config[:chef_server_url] = "https://api.opscode.piab" if KnifeSpecs.const_defined?(:TestYourself) KnifeSpecs.send :remove_const, :TestYourself end Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb')) Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) } end it "confirms that the headers include X-Remote-Request-Id" do expect(Net::HTTP::Get).to receive(:new).with("/monkey", headers).and_return(request_mock) rest.get_rest("monkey") end end describe "when running a command" do before(:each) do if KnifeSpecs.const_defined?(:TestYourself) KnifeSpecs.send :remove_const, :TestYourself end Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb')) Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) } end it "merges the global knife CLI options" do extra_opts = {} extra_opts[:editor] = {:long=>"--editor EDITOR", :description=>"Set the editor to use for interactive commands", :short=>"-e EDITOR", :default=>"/usr/bin/vim"} # there is special hackery to return the subcommand instance going on here. command = Chef::Knife.run(%w{test yourself}, extra_opts) editor_opts = command.options[:editor] expect(editor_opts[:long]).to eq("--editor EDITOR") expect(editor_opts[:description]).to eq("Set the editor to use for interactive commands") expect(editor_opts[:short]).to eq("-e EDITOR") expect(editor_opts[:default]).to eq("/usr/bin/vim") end it "creates an instance of the subcommand and runs it" do command = Chef::Knife.run(%w{test yourself}) expect(command).to be_an_instance_of(KnifeSpecs::TestYourself) expect(command.ran).to be_truthy end it "passes the command specific args to the subcommand" do command = Chef::Knife.run(%w{test yourself with some args}) expect(command.name_args).to eq(%w{with some args}) end it "excludes the command name from the name args when parts are joined with underscores" do command = Chef::Knife.run(%w{test_yourself with some args}) expect(command.name_args).to eq(%w{with some args}) end it "exits if no subcommand matches the CLI args" do stdout = StringIO.new allow(Chef::Knife.ui).to receive(:stderr).and_return(stderr) allow(Chef::Knife.ui).to receive(:stdout).and_return(stdout) expect(Chef::Knife.ui).to receive(:fatal) expect {Chef::Knife.run(%w{fuuu uuuu fuuuu})}.to raise_error(SystemExit) { |e| expect(e.status).not_to eq(0) } end it "loads lazy dependencies" do Chef::Knife.run(%w{test yourself}) expect(KnifeSpecs::TestYourself.test_deps_loaded).to be_truthy end it "loads lazy dependencies from multiple deps calls" do other_deps_loaded = false KnifeSpecs::TestYourself.class_eval do deps { other_deps_loaded = true } end Chef::Knife.run(%w{test yourself}) expect(KnifeSpecs::TestYourself.test_deps_loaded).to be_truthy expect(other_deps_loaded).to be_truthy end describe "merging configuration options" do before do KnifeSpecs::TestYourself.option(:opt_with_default, :short => "-D VALUE", :default => "default-value") end it "prefers the default value if no config or command line value is present" do knife_command = KnifeSpecs::TestYourself.new([]) #empty argv knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("default-value") end it "prefers a value in Chef::Config[:knife] to the default" do Chef::Config[:knife][:opt_with_default] = "from-knife-config" knife_command = KnifeSpecs::TestYourself.new([]) #empty argv knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("from-knife-config") end it "prefers a value from command line over Chef::Config and the default" do Chef::Config[:knife][:opt_with_default] = "from-knife-config" knife_command = KnifeSpecs::TestYourself.new(["-D", "from-cli"]) knife_command.configure_chef expect(knife_command.config[:opt_with_default]).to eq("from-cli") end it "merges `listen` config to Chef::Config" do Chef::Knife.run(%w[test yourself --no-listen], Chef::Application::Knife.options) expect(Chef::Config[:listen]).to be(false) end context "verbosity is greater than zero" do let(:fake_config) { "/does/not/exist/knife.rb" } before do knife.config[:verbosity] = 1 knife.config[:config_file] = fake_config config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config) allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config) allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader) end it "prints the path to the configuration file used" do stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new knife.ui = Chef::Knife::UI.new(stdout, stderr, stdin, {}) expect(Chef::Log).to receive(:info).with("Using configuration from #{fake_config}") knife.configure_chef end end end end describe "when first created" do let(:knife) { KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming}) } before do unless KnifeSpecs.const_defined?(:TestYourself) Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb')) end end it "it parses the options passed to it" do expect(knife.config[:scro]).to eq('scrogramming') end it "extracts its command specific args from the full arg list" do expect(knife.name_args).to eq(%w{with some args}) end it "does not have lazy dependencies loaded" do expect(knife.class.test_deps_loaded).not_to be_truthy end end describe "when formatting exceptions" do let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:stdin) { StringIO.new } let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) } before do knife.ui = ui expect(knife).to receive(:exit).with(100) end it "formats 401s nicely" do response = Net::HTTPUnauthorized.new("1.1", "401", "Unauthorized") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no syncronize your clock?")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(/ERROR: Failed to authenticate to/) expect(stderr.string).to match(/Response: y u no syncronize your clock\?/) end it "formats 403s nicely" do response = Net::HTTPForbidden.new("1.1", "403", "Forbidden") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no administrator")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response)) allow(knife).to receive(:username).and_return("sadpanda") knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action]) expect(stderr.string).to match(%r[Response: y u no administrator]) end it "formats 400s nicely" do response = Net::HTTPBadRequest.new("1.1", "400", "Bad Request") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u search wrong")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: The data in your request was invalid]) expect(stderr.string).to match(%r[Response: y u search wrong]) end it "formats 404s nicely" do response = Net::HTTPNotFound.new("1.1", "404", "Not Found") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nothing to see here")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: The object you are looking for could not be found]) expect(stderr.string).to match(%r[Response: nothing to see here]) end it "formats 500s nicely" do response = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone")) allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: internal server error]) expect(stderr.string).to match(%r[Response: sad trombone]) end it "formats 502s nicely" do response = Net::HTTPBadGateway.new("1.1", "502", "Bad Gateway") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sadder trombone")) allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: bad gateway]) expect(stderr.string).to match(%r[Response: sadder trombone]) end it "formats 503s nicely" do response = Net::HTTPServiceUnavailable.new("1.1", "503", "Service Unavailable") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "saddest trombone")) allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: Service temporarily unavailable]) expect(stderr.string).to match(%r[Response: saddest trombone]) end it "formats other HTTP errors nicely" do response = Net::HTTPPaymentRequired.new("1.1", "402", "Payment Required") response.instance_variable_set(:@read, true) # I hate you, net/http. allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nobugfixtillyoubuy")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response)) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: Payment Required]) expect(stderr.string).to match(%r[Response: nobugfixtillyoubuy]) end it "formats NameError and NoMethodError nicely" do allow(knife).to receive(:run).and_raise(NameError.new("Undefined constant FUUU")) knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: knife encountered an unexpected error]) expect(stderr.string).to match(%r[This may be a bug in the 'knife' knife command or plugin]) expect(stderr.string).to match(%r[Exception: NameError: Undefined constant FUUU]) end it "formats missing private key errors nicely" do allow(knife).to receive(:run).and_raise(Chef::Exceptions::PrivateKeyMissing.new('key not there')) allow(knife).to receive(:api_key).and_return("/home/root/.chef/no-key-here.pem") knife.run_with_pretty_exceptions expect(stderr.string).to match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem]) expect(stderr.string).to match(%r[Check your configuration file and ensure that your private key is readable]) end it "formats connection refused errors nicely" do allow(knife).to receive(:run).and_raise(Errno::ECONNREFUSED.new('y u no shut up')) knife.run_with_pretty_exceptions # Errno::ECONNREFUSED message differs by platform # *nix = Errno::ECONNREFUSED: Connection refused # win32: Errno::ECONNREFUSED: No connection could be made because the target machine actively refused it. expect(stderr.string).to match(%r[ERROR: Network Error: .* - y u no shut up]) expect(stderr.string).to match(%r[Check your knife configuration and network settings]) end it "formats SSL errors nicely and suggests to use `knife ssl check` and `knife ssl fetch`" do error = OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed") allow(knife).to receive(:run).and_raise(error) knife.run_with_pretty_exceptions expected_message=<<-MSG ERROR: Could not establish a secure connection to the server. Use `knife ssl check` to troubleshoot your SSL configuration. If your Chef Server uses a self-signed certificate, you can use `knife ssl fetch` to make knife trust the server's certificates. MSG expect(stderr.string).to include(expected_message) end end end chef-12.3.0/spec/unit/chef_spec.rb0000644000004100000410000000147512520074675016726 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef do it "should have a version defined" do expect(Chef::VERSION).to match(/(\d+)\.(\d+)\.(\d+)/) end end chef-12.3.0/spec/unit/node_map_spec.rb0000644000004100000410000001244012520074675017575 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/node_map' describe Chef::NodeMap do let(:node_map) { Chef::NodeMap.new } let(:node) { Chef::Node.new } describe "with a bad filter name" do it "should raise an error" do expect{ node_map.set(node, :thing, on_platform_family: 'rhel') }.to raise_error end end describe "when no matchers are set at all" do before do node_map.set(:thing, :foo) end it "returns the value" do expect(node_map.get(node, :thing)).to eql(:foo) end it "returns nil for keys that do not exist" do expect(node_map.get(node, :other_thing)).to eql(nil) end end describe "filtering by os" do before do node_map.set(:thing, :foo, os: ["windows"]) node_map.set(:thing, :bar, os: "linux") end it "returns the correct value for windows" do allow(node).to receive(:[]).with(:os).and_return("windows") expect(node_map.get(node, :thing)).to eql(:foo) end it "returns the correct value for linux" do allow(node).to receive(:[]).with(:os).and_return("linux") expect(node_map.get(node, :thing)).to eql(:bar) end it "returns nil for a non-matching os" do allow(node).to receive(:[]).with(:os).and_return("freebsd") expect(node_map.get(node, :thing)).to eql(nil) end end describe "rejecting an os" do before do node_map.set(:thing, :foo, os: "!windows") end it "returns nil for windows" do allow(node).to receive(:[]).with(:os).and_return("windows") expect(node_map.get(node, :thing)).to eql(nil) end it "returns the correct value for linux" do allow(node).to receive(:[]).with(:os).and_return("linux") expect(node_map.get(node, :thing)).to eql(:foo) end end describe "filtering by os and platform_family" do before do node_map.set(:thing, :bar, os: "linux", platform_family: "rhel") end it "returns the correct value when both match" do allow(node).to receive(:[]).with(:os).and_return("linux") allow(node).to receive(:[]).with(:platform_family).and_return("rhel") expect(node_map.get(node, :thing)).to eql(:bar) end it "returns nil for a non-matching os" do allow(node).to receive(:[]).with(:os).and_return("freebsd") expect(node_map.get(node, :thing)).to eql(nil) end it "returns nil when the platform_family does not match" do allow(node).to receive(:[]).with(:os).and_return("linux") allow(node).to receive(:[]).with(:platform_family).and_return("debian") expect(node_map.get(node, :thing)).to eql(nil) end end describe "with a block doing platform_version checks" do before do node_map.set(:thing, :foo, platform_family: "rhel") do |node| node[:platform_version].to_i >= 7 end end it "returns the value when the node matches" do allow(node).to receive(:[]).with(:platform_family).and_return("rhel") allow(node).to receive(:[]).with(:platform_version).and_return("7.0") expect(node_map.get(node, :thing)).to eql(:foo) end it "returns nil when the block does not match" do allow(node).to receive(:[]).with(:platform_family).and_return("rhel") allow(node).to receive(:[]).with(:platform_version).and_return("6.4") expect(node_map.get(node, :thing)).to eql(nil) end it "returns nil when the platform_family filter does not match" do allow(node).to receive(:[]).with(:platform_family).and_return("debian") allow(node).to receive(:[]).with(:platform_version).and_return("7.0") expect(node_map.get(node, :thing)).to eql(nil) end it "returns nil when both do not match" do allow(node).to receive(:[]).with(:platform_family).and_return("debian") allow(node).to receive(:[]).with(:platform_version).and_return("6.0") expect(node_map.get(node, :thing)).to eql(nil) end end describe "resource back-compat testing" do it "should handle :on_platforms => :all" do node_map.set(:chef_gem, :foo, :on_platforms => :all) allow(node).to receive(:[]).with(:platform).and_return("windows") expect(node_map.get(node, :chef_gem)).to eql(:foo) end it "should handle :on_platforms => [ 'windows' ]" do node_map.set(:dsc_script, :foo, :on_platforms => [ 'windows' ]) allow(node).to receive(:[]).with(:platform).and_return("windows") expect(node_map.get(node, :dsc_script)).to eql(:foo) end it "should handle :on_platform => :all" do node_map.set(:link, :foo, :on_platform => :all) allow(node).to receive(:[]).with(:platform).and_return("windows") expect(node_map.get(node, :link)).to eql(:foo) end end end chef-12.3.0/spec/unit/org_spec.rb0000644000004100000410000001316212520074675016604 0ustar www-datawww-data# # Author:: Steven Danna (steve@opscode.com) # Copyright:: Copyright (c) 2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/org' require 'tempfile' describe Chef::Org do let(:org) { Chef::Org.new("an_org") } describe "initialize" do it "is a Chef::Org" do expect(org).to be_a_kind_of(Chef::Org) end end describe "name" do it "lets you set the name to a string" do org.name "sg1" expect(org.name).to eq("sg1") end # It is not feasible to check all invalid characters. Here are a few # that we probably care about. it "raises on invalid characters" do # capital letters expect { org.name "Bar" }.to raise_error(ArgumentError) # slashes expect { org.name "foo/bar" }.to raise_error(ArgumentError) # ? expect { org.name "foo?" }.to raise_error(ArgumentError) # & expect { org.name "foo&" }.to raise_error(ArgumentError) # spaces expect { org.name "foo " }.to raise_error(ArgumentError) end it "raises an ArgumentError if you feed it anything but a string" do expect { org.name Hash.new }.to raise_error(ArgumentError) end end describe "full_name" do it "lets you set the full name" do org.full_name "foo" expect(org.full_name).to eq("foo") end it "raises an ArgumentError if you feed it anything but a string" do expect { org.name Hash.new }.to raise_error(ArgumentError) end end describe "private_key" do it "returns the private key" do org.private_key("super private") expect(org.private_key).to eq("super private") end it "raises an ArgumentError if you feed it something lame" do expect { org.private_key Hash.new }.to raise_error(ArgumentError) end end describe "when serializing to JSON" do let(:json) do org.name("black") org.full_name("black crowes") org.to_json end it "serializes as a JSON object" do expect(json).to match(/^\{.+\}$/) end it "includes the name value" do expect(json).to include(%q{"name":"black"}) end it "includes the full name value" do expect(json).to include(%q{"full_name":"black crowes"}) end it "includes the private key when present" do org.private_key("monkeypants") expect(org.to_json).to include(%q{"private_key":"monkeypants"}) end it "does not include the private key if not present" do expect(json).to_not include("private_key") end end describe "when deserializing from JSON" do let(:org) do o = { "name" => "turtle", "full_name" => "turtle_club", "private_key" => "pandas" } Chef::Org.from_json(o.to_json) end it "deserializes to a Chef::Org object" do expect(org).to be_a_kind_of(Chef::Org) end it "preserves the name" do expect(org.name).to eq("turtle") end it "preserves the full_name" do expect(org.full_name).to eq("turtle_club") end it "includes the private key if present" do expect(org.private_key).to eq("pandas") end end describe "API Interactions" do let(:rest) do Chef::Config[:chef_server_root] = "http://www.example.com" r = double('rest') allow(Chef::REST).to receive(:new).and_return(r) r end let(:org) do o = Chef::Org.new("foobar") o.full_name "foo bar bat" o end describe "list" do let(:response) { {"foobar" => "http://www.example.com/organizations/foobar"} } let(:inflated_response) { {"foobar" => org } } it "lists all orgs" do expect(rest).to receive(:get_rest).with("organizations").and_return(response) expect(Chef::Org.list).to eq(response) end it "inflate all orgs" do allow(Chef::Org).to receive(:load).with("foobar").and_return(org) expect(rest).to receive(:get_rest).with("organizations").and_return(response) expect(Chef::Org.list(true)).to eq(inflated_response) end end describe "create" do it "creates a new org via the API" do expect(rest).to receive(:post_rest).with("organizations", {:name => "foobar", :full_name => "foo bar bat"}).and_return({}) org.create end end describe "read" do it "loads a named org from the API" do expect(rest).to receive(:get_rest).with("organizations/foobar").and_return({"name" => "foobar", "full_name" => "foo bar bat", "private_key" => "private"}) org = Chef::Org.load("foobar") expect(org.name).to eq("foobar") expect(org.full_name).to eq("foo bar bat") expect(org.private_key).to eq("private") end end describe "update" do it "updates an existing org on via the API" do expect(rest).to receive(:put_rest).with("organizations/foobar", {:name => "foobar", :full_name => "foo bar bat"}).and_return({}) org.update end end describe "destroy" do it "deletes the specified org via the API" do expect(rest).to receive(:delete_rest).with("organizations/foobar") org.destroy end end end end chef-12.3.0/spec/unit/cookbook/0000755000004100000410000000000012520074675016261 5ustar www-datawww-datachef-12.3.0/spec/unit/cookbook/synchronizer_spec.rb0000644000004100000410000004613112520074675022362 0ustar www-datawww-datarequire 'spec_helper' require 'chef/cookbook/synchronizer' require 'chef/cookbook_version' describe Chef::CookbookCacheCleaner do describe "when cleaning up unused cookbook components" do let(:cleaner) do cleaner = Chef::CookbookCacheCleaner.instance cleaner.reset! cleaner end let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } let(:unused_template_files) do %w{ cookbooks/unused/templates/default/foo.conf.erb cookbooks/unused/tempaltes/default/bar.conf.erb } end let(:valid_cached_cb_files) do %w{ cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb } end before do valid_cached_cb_files.each do |cbf| cleaner.mark_file_as_valid(cbf) end end it "removes all files not validated during the chef run" do expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + unused_template_files) unused_template_files.each do |cbf| expect(file_cache).to receive(:delete).with(cbf) end cookbook_hash = {"valid1"=> {}, "valid2" => {}} allow(cleaner).to receive(:cache).and_return(file_cache) cleaner.cleanup_file_cache end it "does not remove anything when skip_removal is true" do cleaner.skip_removal = true allow(cleaner.cache).to receive(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb}) expect(cleaner.cache).not_to receive(:delete) cleaner.cleanup_file_cache end it "does not remove anything on chef-solo" do Chef::Config[:solo] = true allow(cleaner.cache).to receive(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb}) expect(cleaner.cache).not_to receive(:delete) cleaner.cleanup_file_cache end end end describe Chef::CookbookSynchronizer do let(:cookbook_a_default_recipe) do { "path" => "recipes/default.rb", "url" => "http://chef.example.com/abc123", "checksum" => "abc123", } end let(:cookbook_a_default_attrs) do { "path" => "attributes/default.rb", "url" => "http://chef.example.com/abc456", "checksum" => "abc456", } end let(:cookbook_a_template) do { "path" => "templates/default/apache2.conf.erb", "url" => "http://chef.example.com/ffffff", "checksum" => "abc125", } end let(:cookbook_a_file) do { "path" => "files/default/megaman.conf", "url" => "http://chef.example.com/megaman.conf", "checksum" => "abc124", } end let(:cookbook_a_manifest) do segments = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ] cookbook_a_manifest = segments.inject({}) {|h, segment| h[segment.to_s] = []; h} cookbook_a_manifest["recipes"] = [ cookbook_a_default_recipe ] cookbook_a_manifest["attributes"] = [ cookbook_a_default_attrs ] cookbook_a_manifest["templates"] = [ cookbook_a_template ] cookbook_a_manifest["files"] = [ cookbook_a_file ] cookbook_a_manifest end let(:cookbook_a) do cookbook_a = Chef::CookbookVersion.new("cookbook_a") cookbook_a.manifest = cookbook_a_manifest cookbook_a end let(:cookbook_manifest) do { "cookbook_a" => cookbook_a } end let(:events) { Chef::EventDispatch::Dispatcher.new } let(:no_lazy_load) { true } let(:synchronizer) do Chef::Config[:no_lazy_load] = no_lazy_load Chef::CookbookSynchronizer.new(cookbook_manifest, events) end it "lists the cookbook names" do expect(synchronizer.cookbook_names).to eq(%w[cookbook_a]) end it "lists the cookbook manifests" do expect(synchronizer.cookbooks).to eq([cookbook_a]) end context "#clear_obsoleted_cookbooks" do after do # Singletons == Global State == Bad Chef::CookbookCacheCleaner.instance.skip_removal = nil end it "behaves correctly when remove_obsoleted_files is false" do synchronizer.remove_obsoleted_files = false expect(synchronizer).not_to receive(:remove_old_cookbooks) expect(synchronizer).to receive(:remove_deleted_files) synchronizer.clear_obsoleted_cookbooks expect(Chef::CookbookCacheCleaner.instance.skip_removal).to be true end it "behaves correctly when remove_obsoleted_files is true" do synchronizer.remove_obsoleted_files = true expect(synchronizer).to receive(:remove_old_cookbooks) expect(synchronizer).to receive(:remove_deleted_files) synchronizer.clear_obsoleted_cookbooks expect(Chef::CookbookCacheCleaner.instance.skip_removal).to be nil end end context "#remove_old_cookbooks" do let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } let(:cookbook_manifest) do {"valid1"=> {}, "valid2" => {}} end it "removes unneeded cookbooks" do valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} obsolete_cb_files = %w{cookbooks/old1/recipes/default.rb cookbooks/old2/recipes/default.rb} expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + obsolete_cb_files) expect(file_cache).to receive(:delete).with('cookbooks/old1/recipes/default.rb') expect(file_cache).to receive(:delete).with('cookbooks/old2/recipes/default.rb') allow(synchronizer).to receive(:cache).and_return(file_cache) synchronizer.remove_old_cookbooks end end context "#remove_deleted_files" do let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } let(:cookbook_manifest) do {"valid1"=> {}, "valid2" => {}} end it "removes only deleted files" do valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} obsolete_cb_files = %w{cookbooks/valid1/recipes/deleted.rb cookbooks/valid2/recipes/deleted.rb} expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + obsolete_cb_files) # valid1 is a cookbook in our run_list expect(synchronizer).to receive(:have_cookbook?).with("valid1").at_least(:once).and_return(true) # valid2 is a cookbook not in our run_list (we're simulating an override run_list where valid2 needs to be preserved) expect(synchronizer).to receive(:have_cookbook?).with("valid2").at_least(:once).and_return(false) expect(file_cache).to receive(:delete).with('cookbooks/valid1/recipes/deleted.rb') expect(synchronizer).to receive(:cookbook_segment).with("valid1", "recipes").at_least(:once).and_return([ { "path" => "recipes/default.rb" }]) allow(synchronizer).to receive(:cache).and_return(file_cache) synchronizer.remove_deleted_files end end let(:cookbook_a_default_recipe_tempfile) do double("Tempfile for cookbook_a default.rb recipe", :path => "/tmp/cookbook_a_recipes_default_rb") end let(:cookbook_a_default_attribute_tempfile) do double("Tempfile for cookbook_a default.rb attr file", :path => "/tmp/cookbook_a_attributes_default_rb") end let(:cookbook_a_file_default_tempfile) do double("Tempfile for cookbook_a megaman.conf file", :path => "/tmp/cookbook_a_file_default_tempfile") end let(:cookbook_a_template_default_tempfile) do double("Tempfile for cookbook_a apache.conf.erb template", :path => "/tmp/cookbook_a_template_default_tempfile") end def setup_common_files_missing_expectations # Files are not in the cache: expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/recipes/default.rb"). and_return(false) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/attributes/default.rb"). and_return(false) # Fetch and copy default.rb recipe expect(server_api).to receive(:get_rest). with('http://chef.example.com/abc123', true). and_return(cookbook_a_default_recipe_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/recipes/default.rb", false). and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") # Fetch and copy default.rb attribute file expect(server_api).to receive(:get_rest). with('http://chef.example.com/abc456', true). and_return(cookbook_a_default_attribute_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/attributes/default.rb", false). and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") end def setup_no_lazy_files_and_templates_missing_expectations expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/files/default/megaman.conf"). and_return(false) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). and_return(false) expect(server_api).to receive(:get_rest). with('http://chef.example.com/megaman.conf', true). and_return(cookbook_a_file_default_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/files/default/megaman.conf", false). and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") expect(server_api).to receive(:get_rest). with('http://chef.example.com/ffffff', true). and_return(cookbook_a_template_default_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") end def setup_common_files_chksum_mismatch_expectations # Files are in the cache: expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/recipes/default.rb"). and_return(true) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/attributes/default.rb"). and_return(true) # Fetch and copy default.rb recipe expect(server_api).to receive(:get_rest). with('http://chef.example.com/abc123', true). and_return(cookbook_a_default_recipe_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/recipes/default.rb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") # Current file has fff000, want abc123 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). and_return("fff000") # Fetch and copy default.rb attribute file expect(server_api).to receive(:get_rest). with('http://chef.example.com/abc456', true). and_return(cookbook_a_default_attribute_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/attributes/default.rb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") # Current file has fff000, want abc456 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). and_return("fff000") end def setup_no_lazy_files_and_templates_chksum_mismatch_expectations # Files are in the cache: expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/files/default/megaman.conf"). and_return(true) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). and_return(true) # Fetch and copy megaman.conf expect(server_api).to receive(:get_rest). with('http://chef.example.com/megaman.conf', true). and_return(cookbook_a_file_default_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/files/default/megaman.conf", false). twice. and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") # Fetch and copy apache2.conf template expect(server_api).to receive(:get_rest). with('http://chef.example.com/ffffff', true). and_return(cookbook_a_template_default_tempfile) expect(file_cache).to receive(:move_to). with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") # Current file has fff000 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/default/megaman.conf"). and_return("fff000") # Current file has fff000 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb"). and_return("fff000") end def setup_common_files_present_expectations # Files are in the cache: expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/recipes/default.rb"). and_return(true) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/attributes/default.rb"). and_return(true) # Current file has abc123, want abc123 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). and_return("abc123") # Current file has abc456, want abc456 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). and_return("abc456") # :load called twice expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/recipes/default.rb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/attributes/default.rb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") end def setup_no_lazy_files_and_templates_present_expectations # Files are in the cache: expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/files/default/megaman.conf"). and_return(true) expect(file_cache).to receive(:has_key?). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). and_return(true) # Current file has abc124, want abc124 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/default/megaman.conf"). and_return("abc124") # Current file has abc125, want abc125 expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb"). and_return("abc125") # :load called twice expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/files/default/megaman.conf", false). twice. and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") expect(file_cache).to receive(:load). with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). twice. and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") end describe "when syncing cookbooks with the server" do let(:server_api) { double("Chef::REST (mock)") } let(:file_cache) { double("Chef::FileCache (mock)") } before do # Would rather not stub out methods on the test subject, but setting up # the state is a PITA and tests for this behavior are above. allow(synchronizer).to receive(:clear_obsoleted_cookbooks) allow(synchronizer).to receive(:server_api).and_return(server_api) allow(synchronizer).to receive(:cache).and_return(file_cache) end context "when the cache does not contain the desired files" do before do setup_common_files_missing_expectations end context "Chef::Config[:no_lazy_load] is false" do let(:no_lazy_load) { false } it "fetches eagerly loaded files" do synchronizer.sync_cookbooks end it "does not fetch templates or cookbook files" do # Implicitly tested in previous test; this test is just for behavior specification. expect(server_api).not_to receive(:get_rest). with('http://chef.example.com/ffffff', true) synchronizer.sync_cookbooks end end context "Chef::Config[:no_lazy_load] is true" do let(:no_lazy_load) { true } before do setup_no_lazy_files_and_templates_missing_expectations end it "fetches templates and cookbook files" do synchronizer.sync_cookbooks end end end context "when the cache contains outdated files" do before do setup_common_files_chksum_mismatch_expectations end context "Chef::Config[:no_lazy_load] is true" do let(:no_lazy_load) { true } before do setup_no_lazy_files_and_templates_chksum_mismatch_expectations end it "updates the outdated files" do synchronizer.sync_cookbooks end end context "Chef::Config[:no_lazy_load] is false" do let(:no_lazy_load) { false } it "updates the outdated files" do synchronizer.sync_cookbooks end end end context "when the cache is up to date" do before do setup_common_files_present_expectations end context "Chef::Config[:no_lazy_load] is true" do let(:no_lazy_load) { true } before do setup_no_lazy_files_and_templates_present_expectations end it "does not update files" do expect(file_cache).not_to receive(:move_to) expect(server_api).not_to receive(:get_rest) synchronizer.sync_cookbooks end end context "Chef::Config[:no_lazy_load] is false" do let(:no_lazy_load) { false } it "does not update files" do expect(file_cache).not_to receive(:move_to) expect(server_api).not_to receive(:get_rest) synchronizer.sync_cookbooks end end end end end chef-12.3.0/spec/unit/cookbook/syntax_check_spec.rb0000644000004100000410000002044112520074675022304 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/cookbook/syntax_check" describe Chef::Cookbook::SyntaxCheck do before do allow(Chef::Platform).to receive(:windows?) { false } end let(:cookbook_path) { File.join(CHEF_SPEC_DATA, 'cookbooks', 'openldap') } let(:syntax_check) { Chef::Cookbook::SyntaxCheck.new(cookbook_path) } let(:open_ldap_cookbook_files) do %w{ attributes/default.rb attributes/smokey.rb definitions/client.rb definitions/server.rb libraries/openldap.rb libraries/openldap/version.rb metadata.rb recipes/default.rb recipes/gigantor.rb recipes/one.rb recipes/return.rb }.map{ |f| File.join(cookbook_path, f) } end before do Chef::Log.logger = Logger.new(StringIO.new) @original_log_level = Chef::Log.level Chef::Log.level = :warn # suppress "Syntax OK" messages @attr_files = %w{default.rb smokey.rb}.map { |f| File.join(cookbook_path, 'attributes', f) } @libr_files = %w{openldap.rb openldap/version.rb}.map { |f| File.join(cookbook_path, 'libraries', f) } @defn_files = %w{client.rb server.rb}.map { |f| File.join(cookbook_path, 'definitions', f)} @recipes = %w{default.rb gigantor.rb one.rb return.rb}.map { |f| File.join(cookbook_path, 'recipes', f) } @ruby_files = @attr_files + @libr_files + @defn_files + @recipes + [File.join(cookbook_path, "metadata.rb")] basenames = %w{ helpers_via_partial_test.erb helper_test.erb openldap_stuff.conf.erb openldap_variable_stuff.conf.erb test.erb some_windows_line_endings.erb all_windows_line_endings.erb no_windows_line_endings.erb } @template_files = basenames.map { |f| File.join(cookbook_path, 'templates', 'default', f)} end after do Chef::Log.level = @original_log_level end it "creates a syntax checker given the cookbook name when Chef::Config.cookbook_path is set" do Chef::Config[:cookbook_path] = File.dirname(cookbook_path) syntax_check = Chef::Cookbook::SyntaxCheck.for_cookbook(:openldap) expect(syntax_check.cookbook_path).to eq(cookbook_path) expect(syntax_check.ruby_files.sort).to eq(open_ldap_cookbook_files.sort) end it "creates a syntax checker given the cookbook name and cookbook_path" do syntax_check = Chef::Cookbook::SyntaxCheck.for_cookbook(:openldap, File.join(CHEF_SPEC_DATA, 'cookbooks')) expect(syntax_check.cookbook_path).to eq(cookbook_path) expect(syntax_check.ruby_files.sort).to eq(open_ldap_cookbook_files.sort) end context "when using a standalone cookbook" do let(:cookbook_path) { File.join(CHEF_SPEC_DATA, 'standalone_cookbook') } it "creates a syntax checker given the cookbook name and cookbook_path for a standalone cookbook" do syntax_check = Chef::Cookbook::SyntaxCheck.for_cookbook(:standalone_cookbook, CHEF_SPEC_DATA) expect(syntax_check.cookbook_path).to eq(cookbook_path) expect(syntax_check.ruby_files).to eq([File.join(cookbook_path, 'recipes/default.rb')]) end end describe "when first created" do it "has the path to the cookbook to syntax check" do expect(syntax_check.cookbook_path).to eq(cookbook_path) end it "lists the ruby files in the cookbook" do expect(syntax_check.ruby_files.sort).to eq(@ruby_files.sort) end it "lists the erb templates in the cookbook" do expect(syntax_check.template_files.sort).to eq(@template_files.sort) end end describe "when validating cookbooks" do let(:cache_path) { Dir.mktmpdir } before do Chef::Config[:syntax_check_cache_path] = cache_path end after do FileUtils.rm_rf(cache_path) if File.exist?(cache_path) end describe "and the files have not been syntax checked previously" do it "shows that all ruby files require a syntax check" do expect(syntax_check.untested_ruby_files.sort).to eq(@ruby_files.sort) end it "shows that all template files require a syntax check" do expect(syntax_check.untested_template_files.sort).to eq(@template_files.sort) end it "removes a ruby file from the list of untested files after it is marked as validated" do recipe = File.join(cookbook_path, 'recipes', 'default.rb') syntax_check.validated(recipe) expect(syntax_check.untested_ruby_files).not_to include(recipe) end it "removes a template file from the list of untested files after it is marked as validated" do template = File.join(cookbook_path, 'templates', 'default', 'test.erb') syntax_check.validated(template) expect(syntax_check.untested_template_files).not_to include(template) end it "validates all ruby files" do expect(syntax_check.validate_ruby_files).to be_truthy expect(syntax_check.untested_ruby_files).to be_empty end it "validates all templates" do expect(syntax_check.validate_templates).to be_truthy expect(syntax_check.untested_template_files).to be_empty end describe "and a file has a syntax error" do before do cookbook_path = File.join(CHEF_SPEC_DATA, 'cookbooks', 'borken') syntax_check.cookbook_path.replace(cookbook_path) end it "it indicates that a ruby file has a syntax error" do expect(syntax_check.validate_ruby_files).to be_falsey end it "does not remove the invalid file from the list of untested files" do expect(syntax_check.untested_ruby_files).to include(File.join(cookbook_path, 'recipes', 'default.rb')) syntax_check.validate_ruby_files expect(syntax_check.untested_ruby_files).to include(File.join(cookbook_path, 'recipes', 'default.rb')) end it "indicates that a template file has a syntax error" do expect(syntax_check.validate_templates).to be_falsey end it "does not remove the invalid template from the list of untested templates" do expect(syntax_check.untested_template_files).to include(File.join(cookbook_path, 'templates', 'default', 'borken.erb')) expect {syntax_check.validate_templates}.not_to change(syntax_check, :untested_template_files) end end describe "and an ignored file has a syntax error" do before do cookbook_path = File.join(CHEF_SPEC_DATA, 'cookbooks', 'ignorken') Chef::Config[:cookbook_path] = File.dirname(cookbook_path) syntax_check.cookbook_path.replace(cookbook_path) @ruby_files = [File.join(cookbook_path, 'metadata.rb'), File.join(cookbook_path, 'recipes/default.rb')] end it "shows that ignored ruby files do not require a syntax check" do expect(syntax_check.untested_ruby_files.sort).to eq(@ruby_files.sort) end it "does not indicate that a ruby file has a syntax error" do expect(syntax_check.validate_ruby_files).to be_truthy expect(syntax_check.untested_ruby_files).to be_empty end end end describe "and the files have been syntax checked previously" do before do syntax_check.untested_ruby_files.each { |f| syntax_check.validated(f) } syntax_check.untested_template_files.each { |f| syntax_check.validated(f) } end it "does not syntax check ruby files" do expect(syntax_check).not_to receive(:shell_out) expect(syntax_check.validate_ruby_files).to be_truthy end it "does not syntax check templates" do expect(syntax_check).not_to receive(:shell_out) expect(syntax_check.validate_templates).to be_truthy end end end end chef-12.3.0/spec/unit/cookbook/file_vendor_spec.rb0000644000004100000410000000642412520074675022122 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Cookbook::FileVendor do let(:file_vendor_class) { Class.new(described_class) } context "when configured to fetch files over http" do let(:http) { double("Chef::REST") } before do file_vendor_class.fetch_from_remote(http) end it "sets the vendor class to RemoteFileVendor" do expect(file_vendor_class.vendor_class).to eq(Chef::Cookbook::RemoteFileVendor) end it "sets the initialization options to the given http object" do expect(file_vendor_class.initialization_options).to eq(http) end context "with a manifest from a cookbook version" do # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest let(:manifest) { {:cookbook_name => "bob", :name => "bob-1.2.3"} } it "creates a RemoteFileVendor for a given manifest" do file_vendor = file_vendor_class.create_from_manifest(manifest) expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) expect(file_vendor.rest).to eq(http) expect(file_vendor.cookbook_name).to eq("bob") end end context "with a manifest from a cookbook artifact" do # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest let(:manifest) { {:name => "bob"} } it "creates a RemoteFileVendor for a given manifest" do file_vendor = file_vendor_class.create_from_manifest(manifest) expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) expect(file_vendor.rest).to eq(http) expect(file_vendor.cookbook_name).to eq("bob") end end end context "when configured to load files from disk" do let(:cookbook_path) { %w[/var/chef/cookbooks /var/chef/other_cookbooks] } # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest let(:manifest) { {:cookbook_name => "bob"} } before do file_vendor_class.fetch_from_disk(cookbook_path) end it "sets the vendor class to FileSystemFileVendor" do expect(file_vendor_class.vendor_class).to eq(Chef::Cookbook::FileSystemFileVendor) end it "sets the initialization options to the given cookbook paths" do expect(file_vendor_class.initialization_options).to eq(cookbook_path) end it "creates a FileSystemFileVendor for a given manifest" do file_vendor = file_vendor_class.create_from_manifest(manifest) expect(file_vendor).to be_a_kind_of(Chef::Cookbook::FileSystemFileVendor) expect(file_vendor.cookbook_name).to eq("bob") expect(file_vendor.repo_paths).to eq(cookbook_path) end end end chef-12.3.0/spec/unit/cookbook/cookbook_version_loader_spec.rb0000644000004100000410000001661012520074675024525 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Cookbook::CookbookVersionLoader do before do allow(Chef::Platform).to receive(:windows?) { false } end describe "loading a cookbook" do let(:chefignore) { nil } let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks/openldap") } let(:cookbook_loader) { Chef::Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore) } let(:loaded_cookbook) do cookbook_loader.load! cookbook_loader.cookbook_version end def full_path(cookbook_relative_path) File.join(cookbook_path, cookbook_relative_path) end it "loads attribute files of the cookbook" do expect(loaded_cookbook.attribute_filenames).to include(full_path("/attributes/default.rb")) expect(loaded_cookbook.attribute_filenames).to include(full_path("/attributes/smokey.rb")) end it "loads definition files" do expect(loaded_cookbook.definition_filenames).to include(full_path("/definitions/client.rb")) expect(loaded_cookbook.definition_filenames).to include(full_path("/definitions/server.rb")) end it "loads recipes" do expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/default.rb")) expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/gigantor.rb")) expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/one.rb")) expect(loaded_cookbook.recipe_filenames).to include(full_path("/recipes/return.rb")) end it "loads libraries" do expect(loaded_cookbook.library_filenames).to include(full_path('/libraries/openldap.rb')) expect(loaded_cookbook.library_filenames).to include(full_path('/libraries/openldap/version.rb')) end it "loads static files in the files/ dir" do expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file1.txt")) expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/remotesubdir/remote_subdir_file2.txt")) end it "loads files that start with a ." do expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/.dotfile")) expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/.ssh/id_rsa")) expect(loaded_cookbook.file_filenames).to include(full_path("/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir")) end it "should load the metadata for the cookbook" do expect(loaded_cookbook.metadata.name.to_s).to eq("openldap") expect(loaded_cookbook.metadata).to be_a_kind_of(Chef::Cookbook::Metadata) end context "when a cookbook has ignored files" do let(:chefignore) { Chef::Cookbook::Chefignore.new(File.join(CHEF_SPEC_DATA, "cookbooks")) } let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "kitchen/openldap") } it "skips ignored files" do expect(loaded_cookbook.recipe_filenames).to include(full_path("recipes/gigantor.rb")) expect(loaded_cookbook.recipe_filenames).to include(full_path("recipes/woot.rb")) expect(loaded_cookbook.recipe_filenames).to_not include(full_path("recipes/ignoreme.rb")) end end context "when the given path is not actually a cookbook" do let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks/NOTHING_HERE_FOLKS") } it "raises an error when loading with #load!" do expect { cookbook_loader.load! }.to raise_error(Chef::Exceptions::CookbookNotFoundInRepo) end it "skips the cookbook when called with #load" do expect { cookbook_loader.load }.to_not raise_error end end context "when a cookbook has a metadata name different than directory basename" do let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "cookbooks/name-mismatch-versionnumber") } it "prefers the metadata name to the directory basename" do expect(loaded_cookbook.name).to eq(:"name-mismatch") end end context "when a cookbook has a metadata file with a ruby error [CHEF-2923]" do let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "invalid-metadata-chef-repo/invalid-metadata") } it "raises an error when loading with #load!" do expect { cookbook_loader.load! }.to raise_error("THIS METADATA HAS A BUG") end it "raises an error when called with #load" do expect { cookbook_loader.load }.to raise_error("THIS METADATA HAS A BUG") end it "doesn't raise an error when determining the cookbook name" do expect { cookbook_loader.cookbook_name }.to_not raise_error end it "doesn't raise an error when metadata is first generated" do expect { cookbook_loader.metadata }.to_not raise_error end it "sets an error flag containing error information" do cookbook_loader.metadata expect(cookbook_loader.metadata_error).to be_a(StandardError) expect(cookbook_loader.metadata_error.message).to eq("THIS METADATA HAS A BUG") end end context "when a cookbook has a metadata file with invalid metadata" do let(:cookbook_path) { File.join(CHEF_SPEC_DATA, "incomplete-metadata-chef-repo/incomplete-metadata") } let(:error_message) do "Cookbook loaded at path(s) [#{cookbook_path}] has invalid metadata: The `name' attribute is required in cookbook metadata" end it "raises an error when loading with #load!" do expect { cookbook_loader.load! }.to raise_error(Chef::Exceptions::MetadataNotValid, error_message) end it "raises an error when called with #load" do expect { cookbook_loader.load }.to raise_error(Chef::Exceptions::MetadataNotValid, error_message) end it "uses the inferred cookbook name [CHEF-2923]" do # This behavior is intended to support the CHEF-2923 feature where # invalid metadata doesn't prevent you from uploading other cookbooks. # # The metadata is the definitive source of the cookbook name, but if # the metadata is incomplete/invalid, we can't read the name from it. # # The CookbookLoader stores CookbookVersionLoaders in a Hash with # cookbook names as the keys, and finds the loader in this Hash to call # #load on it when the user runs a command like `knife cookbook upload specific-cookbook` # # Most of the time this will work, but if the user tries to upload a # specific cookbook by name, has customized that cookbook's name (so it # doesn't match the inferred name), and that metadata file has a syntax # error, we might report a "cookbook not found" error instead of the # metadata syntax error that is the actual cause. expect(cookbook_loader.cookbook_name).to eq(:"incomplete-metadata") end end end end chef-12.3.0/spec/unit/cookbook/chefignore_spec.rb0000644000004100000410000000341612520074675021735 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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 'spec_helper' describe Chef::Cookbook::Chefignore do before do @chefignore = Chef::Cookbook::Chefignore.new(File.join(CHEF_SPEC_DATA, 'cookbooks')) end it "loads the globs in the chefignore file" do expect(@chefignore.ignores).to match_array(%w[recipes/ignoreme.rb ignored]) end it "removes items from an array that match the ignores" do file_list = %w[ recipes/ignoreme.rb recipes/dontignoreme.rb ] expect(@chefignore.remove_ignores_from(file_list)).to eq(%w[recipes/dontignoreme.rb]) end it "determines if a file is ignored" do expect(@chefignore.ignored?('ignored')).to be_truthy expect(@chefignore.ignored?('recipes/ignoreme.rb')).to be_truthy expect(@chefignore.ignored?('recipes/dontignoreme.rb')).to be_falsey end context "when using the single cookbook pattern" do before do @chefignore = Chef::Cookbook::Chefignore.new(File.join(CHEF_SPEC_DATA, 'standalone_cookbook')) end it "loads the globs in the chefignore file" do expect(@chefignore.ignores).to match_array(%w[recipes/ignoreme.rb ignored vendor/bundle/*]) end end end chef-12.3.0/spec/unit/cookbook/metadata_spec.rb0000644000004100000410000006020412520074675021402 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright 2008-2010 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/cookbook/metadata' describe Chef::Cookbook::Metadata do let(:metadata) { Chef::Cookbook::Metadata.new } describe "when comparing for equality" do before do @fields = [ :name, :description, :long_description, :maintainer, :maintainer_email, :license, :platforms, :dependencies, :recommendations, :suggestions, :conflicting, :providing, :replacing, :attributes, :groupings, :recipes, :version, :source_url, :issues_url ] end it "does not depend on object identity for equality" do expect(metadata).to eq(metadata.dup) end it "is not equal to another object if it isn't have all of the metadata fields" do @fields.each_index do |field_to_remove| fields_to_include = @fields.dup fields_to_include.delete_at(field_to_remove) almost_duck_type = Struct.new(*fields_to_include).new @fields.each do |field| setter = "#{field}=" metadata_value = metadata.send(field) almost_duck_type.send(setter, metadata_value) if almost_duck_type.respond_to?(setter) expect(@mets).not_to eq(almost_duck_type) end end end it "is equal to another object if it has equal values for all metadata fields" do duck_type = Struct.new(*@fields).new @fields.each do |field| setter = "#{field}=" metadata_value = metadata.send(field) duck_type.send(setter, metadata_value) end expect(metadata).to eq(duck_type) end it "is not equal if any values are different" do duck_type_class = Struct.new(*@fields) @fields.each do |field_to_change| duck_type = duck_type_class.new @fields.each do |field| setter = "#{field}=" metadata_value = metadata.send(field) duck_type.send(setter, metadata_value) end duck_type.send("#{field_to_change}=".to_sym, :epic_fail) expect(metadata).not_to eq(duck_type) end end end describe "when first created" do it "has no name" do expect(metadata.name).to eq(nil) end it "has an empty description" do expect(metadata.description).to eq("") end it "has an empty long description" do expect(metadata.long_description).to eq("") end it "defaults to 'all rights reserved' license" do expect(metadata.license).to eq("All rights reserved") end it "has an empty maintainer field" do expect(metadata.maintainer).to eq(nil) end it "has an empty maintainer_email field" do expect(metadata.maintainer).to eq(nil) end it "has an empty platforms list" do expect(metadata.platforms).to eq(Mash.new) end it "has an empty dependencies list" do expect(metadata.dependencies).to eq(Mash.new) end it "has an empty recommends list" do expect(metadata.recommendations).to eq(Mash.new) end it "has an empty suggestions list" do expect(metadata.suggestions).to eq(Mash.new) end it "has an empty conflicts list" do expect(metadata.conflicting).to eq(Mash.new) end it "has an empty replaces list" do expect(metadata.replacing).to eq(Mash.new) end it "has an empty attributes list" do expect(metadata.attributes).to eq(Mash.new) end it "has an empty groupings list" do expect(metadata.groupings).to eq(Mash.new) end it "has an empty recipes list" do expect(metadata.recipes).to eq(Mash.new) end it "has an empty source_url string" do expect(metadata.source_url).to eq('') end it "has an empty issues_url string" do expect(metadata.issues_url).to eq('') end end describe "validation" do context "when no required fields are set" do it "is not valid" do expect(metadata).not_to be_valid end it "has a list of validation errors" do expected_errors = ["The `name' attribute is required in cookbook metadata"] expect(metadata.errors).to eq(expected_errors) end end context "when all required fields are set" do before do metadata.name "a-valid-name" end it "is valid" do expect(metadata).to be_valid end it "has no validation errors" do expect(metadata.errors).to be_empty end end end describe "adding a supported platform" do it "should support adding a supported platform with a single expression" do metadata.supports("ubuntu", ">= 8.04") expect(metadata.platforms["ubuntu"]).to eq('>= 8.04') end end describe "meta-data attributes" do params = { :maintainer => "Adam Jacob", :maintainer_email => "adam@opscode.com", :license => "Apache v2.0", :description => "Foobar!", :long_description => "Much Longer\nSeriously", :version => "0.6.0", :source_url => "http://example.com", :issues_url => "http://example.com/issues" } params.sort { |a,b| a.to_s <=> b.to_s }.each do |field, field_value| describe field do it "should be set-able via #{field}" do expect(metadata.send(field, field_value)).to eql(field_value) end it "should be get-able via #{field}" do metadata.send(field, field_value) expect(metadata.send(field)).to eql(field_value) end end end describe "version transformation" do it "should transform an '0.6' version to '0.6.0'" do expect(metadata.send(:version, "0.6")).to eql("0.6.0") end it "should spit out '0.6.0' after transforming '0.6'" do metadata.send(:version, "0.6") expect(metadata.send(:version)).to eql("0.6.0") end end end describe "describing dependencies" do dep_types = { :depends => [ :dependencies, "foo::bar", "> 0.2" ], :recommends => [ :recommendations, "foo::bar", ">= 0.2" ], :suggests => [ :suggestions, "foo::bar", "> 0.2" ], :conflicts => [ :conflicting, "foo::bar", "~> 0.2" ], :provides => [ :providing, "foo::bar", "<= 0.2" ], :replaces => [ :replacing, "foo::bar", "= 0.2.1" ], } dep_types.sort { |a,b| a.to_s <=> b.to_s }.each do |dep, dep_args| check_with = dep_args.shift describe dep do it "should be set-able via #{dep}" do expect(metadata.send(dep, *dep_args)).to eq(dep_args[1]) end it "should be get-able via #{check_with}" do metadata.send(dep, *dep_args) expect(metadata.send(check_with)).to eq({ dep_args[0] => dep_args[1] }) end end end dep_types = { :depends => [ :dependencies, "foo::bar", ">0.2", "> 0.2" ], :recommends => [ :recommendations, "foo::bar", ">=0.2", ">= 0.2" ], :suggests => [ :suggestions, "foo::bar", ">0.2", "> 0.2" ], :conflicts => [ :conflicting, "foo::bar", "~>0.2", "~> 0.2" ], :provides => [ :providing, "foo::bar", "<=0.2", "<= 0.2" ], :replaces => [ :replacing, "foo::bar", "=0.2.1", "= 0.2.1" ], } dep_types.sort { |a,b| a.to_s <=> b.to_s }.each do |dep, dep_args| check_with = dep_args.shift normalized_version = dep_args.pop describe dep do it "should be set-able and normalized via #{dep}" do expect(metadata.send(dep, *dep_args)).to eq(normalized_version) end it "should be get-able and normalized via #{check_with}" do metadata.send(dep, *dep_args) expect(metadata.send(check_with)).to eq({ dep_args[0] => normalized_version }) end end end describe "in the obsoleted format" do dep_types = { :depends => [ "foo::bar", "> 0.2", "< 1.0" ], :recommends => [ "foo::bar", ">= 0.2", "< 1.0" ], :suggests => [ "foo::bar", "> 0.2", "< 1.0" ], :conflicts => [ "foo::bar", "> 0.2", "< 1.0" ], :provides => [ "foo::bar", "> 0.2", "< 1.0" ], :replaces => [ "foo::bar", "> 0.2.1", "< 1.0" ], } dep_types.each do |dep, dep_args| it "for #{dep} raises an informative error instead of vomiting on your shoes" do expect {metadata.send(dep, *dep_args)}.to raise_error(Chef::Exceptions::ObsoleteDependencySyntax) end end end describe "with obsolete operators" do dep_types = { :depends => [ "foo::bar", ">> 0.2"], :recommends => [ "foo::bar", ">> 0.2"], :suggests => [ "foo::bar", ">> 0.2"], :conflicts => [ "foo::bar", ">> 0.2"], :provides => [ "foo::bar", ">> 0.2"], :replaces => [ "foo::bar", ">> 0.2.1"], } dep_types.each do |dep, dep_args| it "for #{dep} raises an informative error instead of vomiting on your shoes" do expect {metadata.send(dep, *dep_args)}.to raise_error(Chef::Exceptions::InvalidVersionConstraint) end end end end describe "attribute groupings" do it "should allow you set a grouping" do group = { "title" => "MySQL Tuning", "description" => "Setting from the my.cnf file that allow you to tune your mysql server" } expect(metadata.grouping("/db/mysql/databases/tuning", group)).to eq(group) end it "should not accept anything but a string for display_name" do expect { metadata.grouping("db/mysql/databases", :title => "foo") }.not_to raise_error expect { metadata.grouping("db/mysql/databases", :title => Hash.new) }.to raise_error(ArgumentError) end it "should not accept anything but a string for the description" do expect { metadata.grouping("db/mysql/databases", :description => "foo") }.not_to raise_error expect { metadata.grouping("db/mysql/databases", :description => Hash.new) }.to raise_error(ArgumentError) end end describe "cookbook attributes" do it "should allow you set an attributes metadata" do attrs = { "display_name" => "MySQL Databases", "description" => "Description of MySQL", "choice" => ['dedicated', 'shared'], "calculated" => false, "type" => 'string', "required" => 'recommended', "recipes" => [ "mysql::server", "mysql::master" ], "default" => [ ], "source_url" => "http://example.com", "issues_url" => "http://example.com/issues" } expect(metadata.attribute("/db/mysql/databases", attrs)).to eq(attrs) end it "should not accept anything but a string for display_name" do expect { metadata.attribute("db/mysql/databases", :display_name => "foo") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :display_name => Hash.new) }.to raise_error(ArgumentError) end it "should not accept anything but a string for the description" do expect { metadata.attribute("db/mysql/databases", :description => "foo") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :description => Hash.new) }.to raise_error(ArgumentError) end it "should not accept anything but a string for the source_url" do expect { metadata.attribute("db/mysql/databases", :source_url => "foo") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :source_url => Hash.new) }.to raise_error(ArgumentError) end it "should not accept anything but a string for the issues_url" do expect { metadata.attribute("db/mysql/databases", :issues_url => "foo") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :issues_url => Hash.new) }.to raise_error(ArgumentError) end it "should not accept anything but an array of strings for choice" do expect { metadata.attribute("db/mysql/databases", :choice => ['dedicated', 'shared']) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :choice => [10, 'shared']) }.to raise_error(ArgumentError) expect { metadata.attribute("db/mysql/databases", :choice => Hash.new) }.to raise_error(ArgumentError) end it "should set choice to empty array by default" do metadata.attribute("db/mysql/databases", {}) expect(metadata.attributes["db/mysql/databases"][:choice]).to eq([]) end it "should let calculated be true or false" do expect { metadata.attribute("db/mysql/databases", :calculated => true) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :calculated => false) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :calculated => Hash.new) }.to raise_error(ArgumentError) end it "should set calculated to false by default" do metadata.attribute("db/mysql/databases", {}) expect(metadata.attributes["db/mysql/databases"][:calculated]).to eq(false) end it "accepts String for the attribute type" do expect { metadata.attribute("db/mysql/databases", :type => "string") }.not_to raise_error end it "accepts Array for the attribute type" do expect { metadata.attribute("db/mysql/databases", :type => "array") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :type => Array.new) }.to raise_error(ArgumentError) end it "accepts symbol for the attribute type" do expect { metadata.attribute("db/mysql/databases", :type => "symbol") }.not_to raise_error end it "should let type be hash (backwards compatibility only)" do expect { metadata.attribute("db/mysql/databases", :type => "hash") }.not_to raise_error end it "should let required be required, recommended or optional" do expect { metadata.attribute("db/mysql/databases", :required => 'required') }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :required => 'recommended') }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :required => 'optional') }.not_to raise_error end it "should convert required true to required" do expect { metadata.attribute("db/mysql/databases", :required => true) }.not_to raise_error #attrib = metadata.attributes["db/mysql/databases"][:required].should == "required" end it "should convert required false to optional" do expect { metadata.attribute("db/mysql/databases", :required => false) }.not_to raise_error #attrib = metadata.attributes["db/mysql/databases"][:required].should == "optional" end it "should set required to 'optional' by default" do metadata.attribute("db/mysql/databases", {}) expect(metadata.attributes["db/mysql/databases"][:required]).to eq('optional') end it "should make sure recipes is an array" do expect { metadata.attribute("db/mysql/databases", :recipes => []) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :required => Hash.new) }.to raise_error(ArgumentError) end it "should set recipes to an empty array by default" do metadata.attribute("db/mysql/databases", {}) expect(metadata.attributes["db/mysql/databases"][:recipes]).to eq([]) end it "should allow the default value to be a string, array, hash, boolean or numeric" do expect { metadata.attribute("db/mysql/databases", :default => []) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :default => {}) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :default => "alice in chains") }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :default => 1337) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :default => true) }.not_to raise_error expect { metadata.attribute("db/mysql/databases", :required => :not_gonna_do_it) }.to raise_error(ArgumentError) end it "should limit the types allowed in the choice array" do options = { :type => "string", :choice => [ "test1", "test2" ], :default => "test1" } expect { metadata.attribute("test_cookbook/test", options) }.not_to raise_error options = { :type => "boolean", :choice => [ true, false ], :default => true } expect { metadata.attribute("test_cookbook/test", options) }.not_to raise_error options = { :type => "numeric", :choice => [ 1337, 420 ], :default => 1337 } expect { metadata.attribute("test_cookbook/test", options) }.not_to raise_error options = { :type => "numeric", :choice => [ true, "false" ], :default => false } expect { metadata.attribute("test_cookbook/test", options) }.to raise_error end it "should error if default used with calculated" do expect { attrs = { :calculated => true, :default => [ "I thought you said calculated" ] } metadata.attribute("db/mysql/databases", attrs) }.to raise_error(ArgumentError) expect { attrs = { :calculated => true, :default => "I thought you said calculated" } metadata.attribute("db/mysql/databases", attrs) }.to raise_error(ArgumentError) end it "should allow a default that is a choice" do expect { attrs = { :choice => [ "a", "b", "c"], :default => "b" } metadata.attribute("db/mysql/databases", attrs) }.not_to raise_error expect { attrs = { :choice => [ "a", "b", "c", "d", "e"], :default => ["b", "d"] } metadata.attribute("db/mysql/databases", attrs) }.not_to raise_error end it "should error if default is not a choice" do expect { attrs = { :choice => [ "a", "b", "c"], :default => "d" } metadata.attribute("db/mysql/databases", attrs) }.to raise_error(ArgumentError) expect { attrs = { :choice => [ "a", "b", "c", "d", "e"], :default => ["b", "z"] } metadata.attribute("db/mysql/databases", attrs) }.to raise_error(ArgumentError) end end describe "recipes" do let(:cookbook) do c = Chef::CookbookVersion.new('test_cookbook') c.recipe_files = [ "default.rb", "enlighten.rb" ] c end before(:each) do metadata.name("test_cookbook") metadata.recipes_from_cookbook_version(cookbook) end it "should have the names of the recipes" do expect(metadata.recipes["test_cookbook"]).to eq("") expect(metadata.recipes["test_cookbook::enlighten"]).to eq("") end it "should let you set the description for a recipe" do metadata.recipe "test_cookbook", "It, um... tests stuff?" expect(metadata.recipes["test_cookbook"]).to eq("It, um... tests stuff?") end it "should automatically provide each recipe" do expect(metadata.providing.has_key?("test_cookbook")).to eq(true) expect(metadata.providing.has_key?("test_cookbook::enlighten")).to eq(true) end end describe "json" do before(:each) do metadata.version "1.0" metadata.maintainer "Bobo T. Clown" metadata.maintainer_email "bobo@example.com" metadata.long_description "I have a long arm!" metadata.supports :ubuntu, "> 8.04" metadata.depends "bobo", "= 1.0" metadata.depends "bubu", "=1.0" metadata.depends "bobotclown", "= 1.1" metadata.recommends "snark", "< 3.0" metadata.suggests "kindness", "> 2.0" metadata.conflicts "hatred" metadata.provides "foo(:bar, :baz)" metadata.replaces "snarkitron" metadata.recipe "test_cookbook::enlighten", "is your buddy" metadata.attribute "bizspark/has_login", :display_name => "You have nothing" metadata.version "1.2.3" end it "should produce the same output from to_json and Chef::JSONCompat" do expect(metadata.to_json).to eq(Chef::JSONCompat.to_json(metadata)) end describe "serialize" do let(:deserialized_metadata) { Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(metadata)) } it "should serialize to a json hash" do expect(deserialized_metadata).to be_a_kind_of(Hash) end %w{ name description long_description maintainer maintainer_email license platforms dependencies suggestions recommendations conflicting providing replacing attributes recipes version source_url issues_url }.each do |t| it "should include '#{t}'" do expect(deserialized_metadata[t]).to eq(metadata.send(t.to_sym)) end end end describe "deserialize" do let(:deserialized_metadata) { Chef::Cookbook::Metadata.from_json(Chef::JSONCompat.to_json(metadata)) } it "should deserialize to a Chef::Cookbook::Metadata object" do expect(deserialized_metadata).to be_a_kind_of(Chef::Cookbook::Metadata) end %w{ name description long_description maintainer maintainer_email license platforms dependencies suggestions recommendations conflicting providing replacing attributes recipes version source_url issues_url }.each do |t| it "should match '#{t}'" do expect(deserialized_metadata.send(t.to_sym)).to eq(metadata.send(t.to_sym)) end end end describe "from_hash" do before(:each) do @hash = metadata.to_hash end [:dependencies, :recommendations, :suggestions, :conflicting, :replacing].each do |to_check| it "should transform deprecated greater than syntax for :#{to_check.to_s}" do @hash[to_check.to_s]["foo::bar"] = ">> 0.2" deserial = Chef::Cookbook::Metadata.from_hash(@hash) expect(deserial.send(to_check)["foo::bar"]).to eq('> 0.2') end it "should transform deprecated less than syntax for :#{to_check.to_s}" do @hash[to_check.to_s]["foo::bar"] = "<< 0.2" deserial = Chef::Cookbook::Metadata.from_hash(@hash) expect(deserial.send(to_check)["foo::bar"]).to eq('< 0.2') end it "should ignore multiple dependency constraints for :#{to_check.to_s}" do @hash[to_check.to_s]["foo::bar"] = [ ">= 1.0", "<= 5.2" ] deserial = Chef::Cookbook::Metadata.from_hash(@hash) expect(deserial.send(to_check)["foo::bar"]).to eq([]) end it "should accept an empty array of dependency constraints for :#{to_check.to_s}" do @hash[to_check.to_s]["foo::bar"] = [] deserial = Chef::Cookbook::Metadata.from_hash(@hash) expect(deserial.send(to_check)["foo::bar"]).to eq([]) end it "should accept single-element arrays of dependency constraints for :#{to_check.to_s}" do @hash[to_check.to_s]["foo::bar"] = [ ">= 2.0" ] deserial = Chef::Cookbook::Metadata.from_hash(@hash) expect(deserial.send(to_check)["foo::bar"]).to eq(">= 2.0") end end end end end chef-12.3.0/spec/unit/search/0000755000004100000410000000000012520074675015720 5ustar www-datawww-datachef-12.3.0/spec/unit/search/query_spec.rb0000644000004100000410000002120412520074675020423 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2009,2010 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/search/query' describe Chef::Search::Query do let(:rest) { double("Chef::REST") } let(:query) { Chef::Search::Query.new } shared_context "filtered search" do let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" } let(:server_url) { "https://api.opscode.com/organizations/opscode/nodes" } let(:args) { { filter_key => filter_hash } } let(:filter_hash) { { 'env' => [ 'chef_environment' ], 'ruby_plat' => [ 'languages', 'ruby', 'platform' ] } } let(:response) { { "rows" => [ { "url" => "#{server_url}/my-name-is-node", "data" => { "env" => "elysium", "ruby_plat" => "nudibranch" } }, { "url" => "#{server_url}/my-name-is-jonas", "data" => { "env" => "hades", "ruby_plat" => "i386-mingw32" } }, { "url" => "#{server_url}/my-name-is-flipper", "data" => { "env" => "elysium", "ruby_plat" => "centos" } }, { "url" => "#{server_url}/my-name-is-butters", "data" => { "env" => "moon", "ruby_plat" => "solaris2", } } ], "start" => 0, "total" => 4 } } let(:response_rows) { [ { "env" => "elysium", "ruby_plat" => "nudibranch" }, { "env" => "hades", "ruby_plat" => "i386-mingw32"}, { "env" => "elysium", "ruby_plat" => "centos"}, { "env" => "moon", "ruby_plat" => "solaris2"} ] } end before(:each) do allow(Chef::REST).to receive(:new).and_return(rest) allow(rest).to receive(:get_rest).and_return(response) end describe "search" do let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" } let(:query_string_continue) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4" } let(:response) { { "rows" => [ { "name" => "my-name-is-node", "chef_environment" => "elysium", "platform" => "rhel", "automatic" => { "languages" => { "ruby" => { "platform" => "nudibranch", "version" => "1.9.3", "target" => "ming-the-merciless" } } } }, { "name" => "my-name-is-jonas", "chef_environment" => "hades", "platform" => "rhel", "automatic" => { "languages" => { "ruby" => { "platform" => "i386-mingw32", "version" => "1.9.3", "target" => "bilbo" } } } }, { "name" => "my-name-is-flipper", "chef_environment" => "elysium", "platform" => "rhel", "automatic" => { "languages" => { "ruby" => { "platform" => "centos", "version" => "2.0.0", "target" => "uno" } } } }, { "name" => "my-name-is-butters", "chef_environment" => "moon", "platform" => "rhel", "automatic" => { "languages" => { "ruby" => { "platform" => "solaris2", "version" => "2.1.2", "target" => "random" } } } }, ], "start" => 0, "total" => 4 } } let(:big_response) { r = response.dup r["total"] = 8 r } let(:big_response_end) { r = response.dup r["start"] = 4 r["total"] = 8 r } it "accepts a type as the first argument" do expect { query.search("node") }.not_to raise_error expect { query.search(:node) }.not_to raise_error expect { query.search(Hash.new) }.to raise_error(Chef::Exceptions::InvalidSearchQuery, /(Hash)/) end it "queries for every object of a type by default" do expect(rest).to receive(:get_rest).with("search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0").and_return(response) query.search(:node) end it "allows a custom query" do expect(rest).to receive(:get_rest).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0").and_return(response) query.search(:node, "platform:rhel") end it "lets you set a sort order" do expect(rest).to receive(:get_rest).with("search/node?q=platform:rhel&sort=id%20desc&start=0").and_return(response) query.search(:node, "platform:rhel", sort: "id desc") end it "lets you set a starting object" do expect(rest).to receive(:get_rest).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=2").and_return(response) query.search(:node, "platform:rhel", start: 2) end it "lets you set how many rows to return" do expect(rest).to receive(:get_rest).with("search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0&rows=40").and_return(response) query.search(:node, "platform:rhel", rows: 40) end it "throws an exception if you pass an incorrect option" do expect { query.search(:node, "platform:rhel", total: 10) } .to raise_error(ArgumentError, /unknown keyword: total/) end it "returns the raw rows, start, and total if no block is passed" do rows, start, total = query.search(:node) expect(rows).to equal(response["rows"]) expect(start).to equal(response["start"]) expect(total).to equal(response["total"]) end it "calls a block for each object in the response" do @call_me = double("blocky") response["rows"].each { |r| expect(@call_me).to receive(:do).with(r) } query.search(:node) { |r| @call_me.do(r) } end it "pages through the responses" do @call_me = double("blocky") response["rows"].each { |r| expect(@call_me).to receive(:do).with(r) } query.search(:node, "*:*", sort: nil, start: 0, rows: 1) { |r| @call_me.do(r) } end it "sends multiple API requests when the server indicates there is more data" do expect(rest).to receive(:get_rest).with(query_string).and_return(big_response) expect(rest).to receive(:get_rest).with(query_string_continue).and_return(big_response_end) query.search(:node, "platform:rhel") do |r| nil end end context "when :filter_result is provided as a result" do include_context "filtered search" do let(:filter_key) { :filter_result } before(:each) do expect(rest).to receive(:post_rest).with(query_string, args[filter_key]).and_return(response) end it "returns start" do start = query.search(:node, "platform:rhel", args)[1] expect(start).to eq(response['start']) end it "returns total" do total = query.search(:node, "platform:rhel", args)[2] expect(total).to eq(response['total']) end it "returns rows with the filter applied" do filtered_rows = query.search(:node, "platform:rhel", args)[0] expect(filtered_rows).to match_array(response_rows) end end end end describe "#partial_search" do include_context "filtered search" do let(:filter_key) { :keys } it "emits a deprecation warning" do # partial_search calls search, so we'll stub search to return empty allow(query).to receive(:search).and_return( [ [], 0, 0 ] ) expect(Chef::Log).to receive(:warn).with(/DEPRECATED: The 'partial_search' API is deprecated/) query.partial_search(:node, "platform:rhel", args) end it "returns an array of filtered hashes" do expect(rest).to receive(:post_rest).with(query_string, args[filter_key]).and_return(response) results = query.partial_search(:node, "platform:rhel", args) expect(results[0]).to match_array(response_rows) end end end end chef-12.3.0/spec/unit/data_bag_spec.rb0000644000004100000410000002237012520074675017540 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/data_bag' describe Chef::DataBag do before(:each) do @data_bag = Chef::DataBag.new allow(Chef::Platform)::to receive(:windows?) { false } end describe "initialize" do it "should be a Chef::DataBag" do expect(@data_bag).to be_a_kind_of(Chef::DataBag) end end describe "name" do it "should let you set the name to a string" do expect(@data_bag.name("clowns")).to eq("clowns") end it "should return the current name" do @data_bag.name "clowns" expect(@data_bag.name).to eq("clowns") end it "should not accept spaces" do expect { @data_bag.name "clown masters" }.to raise_error(ArgumentError) end it "should throw an ArgumentError if you feed it anything but a string" do expect { @data_bag.name Hash.new }.to raise_error(ArgumentError) end [ ".", "-", "_", "1"].each do |char| it "should allow a '#{char}' character in the data bag name" do expect(@data_bag.name("clown#{char}clown")).to eq("clown#{char}clown") end end end describe "deserialize" do before(:each) do @data_bag.name('mars_volta') @deserial = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@data_bag)) end it "should deserialize to a Chef::DataBag object" do expect(@deserial).to be_a_kind_of(Chef::DataBag) end %w{ name }.each do |t| it "should match '#{t}'" do expect(@deserial.send(t.to_sym)).to eq(@data_bag.send(t.to_sym)) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @data_bag } end end end describe "when saving" do before do @data_bag.name('piggly_wiggly') @rest = double("Chef::REST") allow(Chef::REST).to receive(:new).and_return(@rest) end it "should silently proceed when the data bag already exists" do exception = double("409 error", :code => "409") expect(@rest).to receive(:post_rest).and_raise(Net::HTTPServerException.new("foo", exception)) @data_bag.save end it "should create the data bag" do expect(@rest).to receive(:post_rest).with("data", @data_bag) @data_bag.save end describe "when whyrun mode is enabled" do before do Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end it "should not save" do expect(@rest).not_to receive(:post_rest) @data_bag.save end end end describe "when loading" do describe "from an API call" do before do Chef::Config[:chef_server_url] = 'https://myserver.example.com' @http_client = double('Chef::REST') end it "should get the data bag from the server" do expect(Chef::REST).to receive(:new).with('https://myserver.example.com').and_return(@http_client) expect(@http_client).to receive(:get_rest).with('data/foo') Chef::DataBag.load('foo') end it "should return the data bag" do allow(Chef::REST).to receive(:new).and_return(@http_client) expect(@http_client).to receive(:get_rest).with('data/foo').and_return({'bar' => 'https://myserver.example.com/data/foo/bar'}) data_bag = Chef::DataBag.load('foo') expect(data_bag).to eq({'bar' => 'https://myserver.example.com/data/foo/bar'}) end end def file_dir_stub(path, returns = true) expect(File).to receive(:directory?).with(path).and_return(returns) end def dir_glob_stub(path, returns = []) expect(Dir).to receive(:glob).with(File.join(path, 'foo/*.json')).and_return(returns) end shared_examples_for "data bag in solo mode" do |data_bag_path| before do Chef::Config[:solo] = true Chef::Config[:data_bag_path] = data_bag_path @paths = Array(data_bag_path) end after do Chef::Config[:solo] = false end it "should get the data bag from the data_bag_path" do @paths.each do |path| file_dir_stub(path) dir_glob_stub(path) end Chef::DataBag.load('foo') end it "should get the data bag from the data_bag_path by symbolic name" do @paths.each do |path| file_dir_stub(path) dir_glob_stub(path) end Chef::DataBag.load(:foo) end it "should return the data bag" do @paths.each do |path| file_dir_stub(path) if path == @paths.first dir_glob_stub(path, [File.join(path, 'foo/bar.json'), File.join(path, 'foo/baz.json')]) else dir_glob_stub(path) end end expect(IO).to receive(:read).with(File.join(@paths.first, 'foo/bar.json')).and_return('{"id": "bar", "name": "Bob Bar" }') expect(IO).to receive(:read).with(File.join(@paths.first, 'foo/baz.json')).and_return('{"id": "baz", "name": "John Baz" }') data_bag = Chef::DataBag.load('foo') expect(data_bag).to eq({ 'bar' => { 'id' => 'bar', 'name' => 'Bob Bar' }, 'baz' => { 'id' => 'baz', 'name' => 'John Baz' }}) end it "should raise if data bag has items with similar names but different content" do @paths.each do |path| file_dir_stub(path) item_with_different_content = "{\"id\": \"bar\", \"name\": \"Bob Bar\", \"path\": \"#{path}\"}" expect(IO).to receive(:read).with(File.join(path, 'foo/bar.json')).and_return(item_with_different_content) if data_bag_path.is_a?(String) dir_glob_stub(path, [File.join(path, 'foo/bar.json'), File.join(path, 'foo/baz.json')]) item_2_with_different_content = '{"id": "bar", "name": "John Baz"}' expect(IO).to receive(:read).with(File.join(path, 'foo/baz.json')).and_return(item_2_with_different_content) else dir_glob_stub(path, [File.join(path, 'foo/bar.json')]) end end expect { Chef::DataBag.load('foo') }.to raise_error(Chef::Exceptions::DuplicateDataBagItem) end it "should return data bag if it has items with similar names and the same content" do @paths.each do |path| file_dir_stub(path) dir_glob_stub(path, [File.join(path, 'foo/bar.json'), File.join(path, 'foo/baz.json')]) item_with_same_content = '{"id": "bar", "name": "Bob Bar"}' expect(IO).to receive(:read).with(File.join(path, 'foo/bar.json')).and_return(item_with_same_content) expect(IO).to receive(:read).with(File.join(path, 'foo/baz.json')).and_return(item_with_same_content) end data_bag = Chef::DataBag.load('foo') test_data_bag = { 'bar' => { 'id' => 'bar', 'name' => 'Bob Bar'} } expect(data_bag).to eq(test_data_bag) end it "should merge data bag items if there are no conflicts" do @paths.each_with_index do |path, index| file_dir_stub(path) dir_glob_stub(path, [File.join(path, 'foo/bar.json'), File.join(path, 'foo/baz.json')]) test_item_with_same_content = '{"id": "bar", "name": "Bob Bar"}' expect(IO).to receive(:read).with(File.join(path, 'foo/bar.json')).and_return(test_item_with_same_content) test_uniq_item = "{\"id\": \"baz_#{index}\", \"name\": \"John Baz\", \"path\": \"#{path}\"}" expect(IO).to receive(:read).with(File.join(path, 'foo/baz.json')).and_return(test_uniq_item) end data_bag = Chef::DataBag.load('foo') test_data_bag = { 'bar' => { 'id' => 'bar', 'name' => 'Bob Bar'} } @paths.each_with_index do |path, index| test_data_bag["baz_#{index}"] = { "id" => "baz_#{index}", "name" => "John Baz", "path" => path } end expect(data_bag).to eq(test_data_bag) end it "should return the data bag list" do @paths.each do |path| file_dir_stub(path) expect(Dir).to receive(:glob).and_return([File.join(path, 'foo'), File.join(path, 'bar')]) end data_bag_list = Chef::DataBag.list expect(data_bag_list).to eq({ 'bar' => 'bar', 'foo' => 'foo' }) end it 'should raise an error if the configured data_bag_path is invalid' do file_dir_stub(@paths.first, false) expect { Chef::DataBag.load('foo') }.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid" end end describe "data bag with string path" do it_should_behave_like "data bag in solo mode", "/var/chef/data_bags" end describe "data bag with array path" do it_should_behave_like "data bag in solo mode", ["/var/chef/data_bags", "/var/chef/data_bags_2"] end end end chef-12.3.0/spec/unit/file_content_management/0000755000004100000410000000000012520074675021320 5ustar www-datawww-datachef-12.3.0/spec/unit/file_content_management/deploy/0000755000004100000410000000000012520074675022614 5ustar www-datawww-datachef-12.3.0/spec/unit/file_content_management/deploy/cp_spec.rb0000644000004100000410000000254512520074675024563 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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::FileContentManagement::Deploy::Cp do let(:content_deployer) { described_class.new } let(:target_file_path) { "/etc/my_app.conf" } describe "creating the file" do it "touches the file to create it" do expect(FileUtils).to receive(:touch).with(target_file_path) content_deployer.create(target_file_path) end end describe "updating the file" do let(:staging_file_path) { "/tmp/random-dir/staging-file.tmp" } it "copies the staging file's content" do expect(FileUtils).to receive(:cp).with(staging_file_path, target_file_path) content_deployer.deploy(staging_file_path, target_file_path) end end end chef-12.3.0/spec/unit/file_content_management/deploy/mv_unix_spec.rb0000644000004100000410000000732312520074675025645 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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::FileContentManagement::Deploy::MvUnix do let(:content_deployer) { described_class.new } let(:target_file_path) { "/etc/my_app.conf" } describe "creating the file" do it "touches the file to create it" do expect(FileUtils).to receive(:touch).with(target_file_path) content_deployer.create(target_file_path) end end describe "updating the file" do let(:staging_file_path) { "/tmp/random-dir/staging-file.tmp" } let(:target_file_mode) { 0644 } let(:target_file_stat) do double "File::Stat struct for target file", :mode => target_file_mode, :uid => target_file_uid, :gid => target_file_gid end before do expect(File).to receive(:stat).with(target_file_path).and_return(target_file_stat) expect(File).to receive(:chmod).with(target_file_mode, staging_file_path).and_return(1) expect(FileUtils).to receive(:mv).with(staging_file_path, target_file_path) end # This context represents the case where: # * Chef runs as root # * The owner and group of the target file match the owner and group of the # staging file. context "when the user has permissions to set file ownership" do # For the purposes of this test, the uid/gid can be anything. These # values are just chosen because (assuming chef-client's euid == 1001 and # egid == 1001), the `chown` call is allowed by the OS. See the # description of `EPERM` in `man 2 chown` for reference. let(:target_file_uid) { 1001 } let(:target_file_gid) { 1001 } before do expect(File).to receive(:chown).with(target_file_uid, nil, staging_file_path).and_return(1) expect(File).to receive(:chown).with(nil, target_file_gid, staging_file_path).and_return(1) end it "fixes up permissions and moves the file into place" do content_deployer.deploy(staging_file_path, target_file_path) end end context "when the user does not have permissions to set file ownership" do # The test code does not care what these values are. These values are # chosen because they're representitive of the case that chef-client is # running as non-root and is managing a file that got ownership set to # root somehow. In this example, gid==20 is something like "staff" which # the user running chef-client is a member of (but it's not that user's # primary group). let(:target_file_uid) { 0 } let(:target_file_gid) { 20 } before do expect(File).to receive(:chown).with(target_file_uid, nil, staging_file_path).and_raise(Errno::EPERM) expect(File).to receive(:chown).with(nil, target_file_gid, staging_file_path).and_raise(Errno::EPERM) expect(Chef::Log).to receive(:warn).with(/^Could not set uid/) expect(Chef::Log).to receive(:warn).with(/^Could not set gid/) end it "fixes up permissions and moves the file into place" do content_deployer.deploy(staging_file_path, target_file_path) end end end end chef-12.3.0/spec/unit/file_content_management/deploy/mv_windows_spec.rb0000644000004100000410000001434512520074675026356 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' unless Chef::Platform.windows? class Chef module ReservedNames module Win32 module Security ACL = Object.new SecurableObject = Object.new end end end end end require 'chef/file_content_management/deploy/mv_windows' describe Chef::FileContentManagement::Deploy::MvWindows do let(:content_deployer) { described_class.new } let(:target_file_path) { "/etc/my_app.conf" } describe "creating the file" do it "touches the file to create it" do expect(FileUtils).to receive(:touch).with(target_file_path) content_deployer.create(target_file_path) end end describe "updating the file" do let(:staging_file_path) { "/tmp/random-dir/staging-file.tmp" } let(:target_file_security_object) do double "Securable Object for target file" end let(:updated_target_security_object) do double "Securable Object for target file after staging file deploy" end before do allow(Chef::ReservedNames::Win32::Security::SecurableObject). to receive(:new). with(target_file_path). and_return(target_file_security_object, updated_target_security_object) end context "when run without adminstrator privileges" do before do expect(target_file_security_object).to receive(:security_descriptor).and_raise(Chef::Exceptions::Win32APIError) end it "errors out with a WindowsNotAdmin error" do expect { content_deployer.deploy(staging_file_path, target_file_path)}.to raise_error(Chef::Exceptions::WindowsNotAdmin) end end context "when run with administrator privileges" do let(:original_target_file_owner) { double("original target file owner") } let(:original_target_file_group) { double("original target file group") } let(:target_file_security_descriptor) do double "security descriptor for target file", :group => original_target_file_group, :owner => original_target_file_owner end let(:updated_target_security_descriptor) do double "security descriptor for target file" end before do allow(target_file_security_object).to receive(:security_descriptor).and_return(target_file_security_descriptor) expect(FileUtils).to receive(:mv).with(staging_file_path, target_file_path) expect(updated_target_security_object).to receive(:group=).with(original_target_file_group) expect(updated_target_security_object).to receive(:owner=).with(original_target_file_owner) end context "and the target file has no dacl or sacl" do before do allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(false) allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(false) end it "fixes up permissions and moves the file into place" do content_deployer.deploy(staging_file_path, target_file_path) end end context "and the target has a dacl and sacl" do let(:inherited_dacl_ace) { double("Windows dacl ace (inherited)", :inherited? => true) } let(:not_inherited_dacl_ace) { double("Windows dacl ace (not inherited)", :inherited? => false) } let(:original_target_file_dacl) { [inherited_dacl_ace, not_inherited_dacl_ace] } let(:inherited_sacl_ace) { double("Windows sacl ace (inherited)", :inherited? => true) } let(:not_inherited_sacl_ace) { double("Windows sacl ace (not inherited)", :inherited? => false) } let(:original_target_file_sacl) { [inherited_sacl_ace, not_inherited_sacl_ace] } let(:custom_dacl) { double("Windows ACL for non-inherited dacl aces") } let(:custom_sacl) { double("Windows ACL for non-inherited sacl aces") } before do allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true) allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(dacl_inherits?) allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl) expect(Chef::ReservedNames::Win32::Security::ACL). to receive(:create). with([not_inherited_dacl_ace]). and_return(custom_dacl) allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true) allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(sacl_inherits?) allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl) expect(Chef::ReservedNames::Win32::Security::ACL). to receive(:create). with([not_inherited_sacl_ace]). and_return(custom_sacl) expect(updated_target_security_object).to receive(:set_dacl).with(custom_dacl, dacl_inherits?) expect(updated_target_security_object).to receive(:set_sacl).with(custom_sacl, sacl_inherits?) end context "and the dacl and sacl don't inherit" do let(:dacl_inherits?) { false } let(:sacl_inherits?) { false } it "fixes up permissions and moves the file into place" do content_deployer.deploy(staging_file_path, target_file_path) end end context "and the dacl and sacl inherit" do let(:dacl_inherits?) { true } let(:sacl_inherits?) { true } it "fixes up permissions and moves the file into place" do content_deployer.deploy(staging_file_path, target_file_path) end end end end end end chef-12.3.0/spec/unit/provider_resolver_spec.rb0000644000004100000410000006340112520074675021571 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 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/convert_to_class_name' require 'chef/provider_resolver' include Chef::Mixin::ConvertToClassName describe Chef::ProviderResolver do let(:node) do node = Chef::Node.new allow(node).to receive(:[]).with(:os).and_return(os) allow(node).to receive(:[]).with(:platform_family).and_return(platform_family) allow(node).to receive(:[]).with(:platform).and_return(platform) allow(node).to receive(:[]).with(:platform_version).and_return(platform_version) allow(node).to receive(:is_a?).and_return(Chef::Node) node end let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) } let(:action) { :start } let(:resolved_provider) { provider_resolver.resolve } let(:provider) { nil } let(:resource_name) { :service } let(:resource) { double(Chef::Resource, provider: provider, resource_name: resource_name) } before do allow(resource).to receive(:is_a?).with(Chef::Resource).and_return(true) end describe "resolving service resource" do def stub_service_providers(*services) services ||= [] allow(Chef::Platform::ServiceHelpers).to receive(:service_resource_providers) .and_return(services) end def stub_service_configs(*configs) configs ||= [] allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return(configs) end before do expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) allow(resource).to receive(:service_name).and_return("ntp") end shared_examples_for "an ubuntu platform with upstart, update-rc.d and systemd" do before do stub_service_providers(:debian, :invokercd, :upstart, :systemd) end it "when only the SysV init script exists, it returns a Service::Debian provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd, :systemd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd, :upstart, :systemd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end it "when only the Upstart script exists, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :upstart, :systemd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :systemd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end it "when only the SysV init script exists, it returns a Service::Debian provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Debian) end it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd, :upstart ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end it "when only the Upstart script exists, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :upstart ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Systemd) end end shared_examples_for "an ubuntu platform with upstart and update-rc.d" do before do stub_service_providers(:debian, :invokercd, :upstart) end # needs to be handled by the highest priority init.d handler context "when only the SysV init script exists" do before do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) end it "enables init, invokercd, debian and upstart providers" do expect(provider_resolver.enabled_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, Chef::Provider::Service::Upstart, ) end it "supports all the enabled handlers except for upstart" do expect(provider_resolver.supported_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, ) expect(provider_resolver.supported_handlers).to_not include( Chef::Provider::Service::Upstart, ) end it "returns a Service::Debian provider" do expect(resolved_provider).to eql(Chef::Provider::Service::Debian) end end # on ubuntu this must be handled by upstart, the init script will exit 1 and fail context "when both SysV and Upstart scripts exist" do before do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd, :upstart ] ) end it "enables init, invokercd, debian and upstart providers" do expect(provider_resolver.enabled_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, Chef::Provider::Service::Upstart, ) end it "supports all the enabled handlers" do expect(provider_resolver.supported_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, Chef::Provider::Service::Upstart, ) end it "returns a Service::Upstart provider" do expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end end # this case is a pure-upstart script which is easy context "when only the Upstart script exists" do before do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :upstart ] ) end it "enables init, invokercd, debian and upstart providers" do expect(provider_resolver.enabled_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, Chef::Provider::Service::Upstart, ) end it "supports only the upstart handler" do expect(provider_resolver.supported_handlers).to include( Chef::Provider::Service::Upstart, ) expect(provider_resolver.supported_handlers).to_not include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, ) end it "returns a Service::Upstart provider" do expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end end # this case is important to get correct for why-run when no config is setup context "when both do not exist" do before do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) end it "enables init, invokercd, debian and upstart providers" do expect(provider_resolver.enabled_handlers).to include( Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, Chef::Provider::Service::Upstart, ) end it "no providers claim to support the resource" do expect(provider_resolver.supported_handlers).to_not include( Chef::Provider::Service::Upstart, Chef::Provider::Service::Debian, Chef::Provider::Service::Init, Chef::Provider::Service::Invokercd, ) end it "returns a Debian Provider" do expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end end end shared_examples_for "a debian platform using the insserv provider" do context "with a default install" do before do stub_service_providers(:debian, :invokercd, :insserv) end it "uses the Service::Insserv Provider to manage sysv init scripts" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) end it "uses the Service::Insserv Provider when there is no config" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) end end context "when the user has installed upstart" do before do stub_service_providers(:debian, :invokercd, :insserv, :upstart) end it "when only the SysV init script exists, it returns an Insserv provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Insserv) end it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :initd, :upstart ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end it "when only the Upstart script exists, it returns a Service::Upstart provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ :upstart ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp") .and_return( [ ] ) expect(resolved_provider).to eql(Chef::Provider::Service::Upstart) end end end describe "on Ubuntu 14.10" do let(:os) { "linux" } let(:platform) { "ubuntu" } let(:platform_family) { "debian" } let(:platform_version) { "14.04" } it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd" end describe "on Ubuntu 14.04" do let(:os) { "linux" } let(:platform) { "ubuntu" } let(:platform_family) { "debian" } let(:platform_version) { "14.04" } it_behaves_like "an ubuntu platform with upstart and update-rc.d" end describe "on Ubuntu 10.04" do let(:os) { "linux" } let(:platform) { "ubuntu" } let(:platform_family) { "debian" } let(:platform_version) { "10.04" } it_behaves_like "an ubuntu platform with upstart and update-rc.d" end # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???) describe "on Debian 4.0" do let(:os) { "linux" } let(:platform) { "debian" } let(:platform_family) { "debian" } let(:platform_version) { "4.0" } #it_behaves_like "a debian platform using the debian provider" end # Debian replaced the debian provider with insserv in the FIXME:VERSION distro describe "on Debian 7.0" do let(:os) { "linux" } let(:platform) { "debian" } let(:platform_family) { "debian" } let(:platform_version) { "7.0" } it_behaves_like "a debian platform using the insserv provider" end %w{solaris2 openindiana opensolaris nexentacore omnios smartos}.each do |platform| describe "on #{platform}" do let(:os) { "solaris2" } let(:platform) { platform } let(:platform_family) { platform } let(:platform_version) { "5.11" } it "returns a Solaris provider" do stub_service_providers stub_service_configs expect(resolved_provider).to eql(Chef::Provider::Service::Solaris) end it "always returns a Solaris provider" do # no matter what we stub on the next two lines we should get a Solaris provider stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) expect(resolved_provider).to eql(Chef::Provider::Service::Solaris) end end end %w{mswin mingw32 windows}.each do |platform| describe "on #{platform}" do let(:os) { "windows" } let(:platform) { platform } let(:platform_family) { "windows" } let(:platform_version) { "5.11" } it "returns a Windows provider" do stub_service_providers stub_service_configs expect(resolved_provider).to eql(Chef::Provider::Service::Windows) end it "always returns a Windows provider" do # no matter what we stub on the next two lines we should get a Windows provider stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) expect(resolved_provider).to eql(Chef::Provider::Service::Windows) end end end %w{mac_os_x mac_os_x_server}.each do |platform| describe "on #{platform}" do let(:os) { "darwin" } let(:platform) { platform } let(:platform_family) { "mac_os_x" } let(:platform_version) { "10.9.2" } it "returns a Macosx provider" do stub_service_providers stub_service_configs expect(resolved_provider).to eql(Chef::Provider::Service::Macosx) end it "always returns a Macosx provider" do # no matter what we stub on the next two lines we should get a Macosx provider stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd) expect(resolved_provider).to eql(Chef::Provider::Service::Macosx) end end end %w{freebsd netbsd}.each do |platform| describe "on #{platform}" do let(:os) { platform } let(:platform) { platform } let(:platform_family) { platform } let(:platform_version) { "10.0-RELEASE" } it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do stub_service_providers stub_service_configs(:usr_local_etc_rcd) expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) end it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do stub_service_providers stub_service_configs(:etc_rcd) expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) end it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do # should only care about :usr_local_etc_rcd stub in the service configs stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd) expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) end it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do # should only care about :etc_rcd stub in the service configs stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd) stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd) expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) end it "foo" do stub_service_providers stub_service_configs expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd) end end end end describe "for the package provider" do let(:resource_name) { :package } before do expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) end %w{mac_os_x mac_os_x_server}.each do |platform| describe "on #{platform}" do let(:os) { "darwin" } let(:platform) { platform } let(:platform_family) { "mac_os_x" } let(:platform_version) { "10.9.2" } it "returns a Chef::Provider::Package::Homebrew provider" do expect(resolved_provider).to eql(Chef::Provider::Package::Homebrew) end end end end provider_mapping = { "mac_os_x" => { :package => Chef::Provider::Package::Homebrew, :user => Chef::Provider::User::Dscl, :group => Chef::Provider::Group::Dscl, }, "mac_os_x_server" => { :package => Chef::Provider::Package::Homebrew, :user => Chef::Provider::User::Dscl, :group => Chef::Provider::Group::Dscl, }, "mswin" => { :env => Chef::Provider::Env::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, :batch => Chef::Provider::Batch, :powershell_script => Chef::Provider::PowershellScript, }, "mingw32" => { :env => Chef::Provider::Env::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, :batch => Chef::Provider::Batch, :powershell_script => Chef::Provider::PowershellScript, }, "windows" => { :env => Chef::Provider::Env::Windows, :user => Chef::Provider::User::Windows, :group => Chef::Provider::Group::Windows, :mount => Chef::Provider::Mount::Windows, :batch => Chef::Provider::Batch, :powershell_script => Chef::Provider::PowershellScript, }, "aix" => { :cron => Chef::Provider::Cron::Aix, }, "netbsd"=> { :group => Chef::Provider::Group::Groupmod, }, "openbsd" => { :group => Chef::Provider::Group::Usermod, :package => Chef::Provider::Package::Openbsd, }, } def self.do_platform(platform_hash) platform_hash.each do |resource, provider| describe "for #{resource}" do let(:resource_name) { resource } it "resolves to a #{provider}" do expect(resolved_provider).to eql(provider) end end end end describe "individual platform mappings" do let(:resource_name) { :user } before do expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) end %w{mac_os_x mac_os_x_server}.each do |platform| describe "on #{platform}" do let(:os) { "darwin" } let(:platform) { platform } let(:platform_family) { "mac_os_x" } let(:platform_version) { "10.9.2" } do_platform(provider_mapping[platform]) end end %w{mswin mingw32 windows}.each do |platform| describe "on #{platform}" do let(:os) { "windows" } let(:platform) { platform } let(:platform_family) { "windows" } let(:platform_version) { "10.9.2" } do_platform(provider_mapping[platform]) end end describe "on AIX" do let(:os) { "aix" } let(:platform) { "aix" } let(:platform_family) { "aix" } let(:platform_version) { "6.2" } do_platform(provider_mapping['aix']) end %w{netbsd openbsd}.each do |platform| describe "on #{platform}" do let(:os) { platform } let(:platform) { platform } let(:platform_family) { platform } let(:platform_version) { "10.0-RELEASE" } do_platform(provider_mapping[platform]) end end end describe "resolving static providers" do def resource_class(resource) Chef::Resource.const_get(convert_to_class_name(resource.to_s)) end static_mapping = { apt_package: Chef::Provider::Package::Apt, bash: Chef::Provider::Script, bff_package: Chef::Provider::Package::Aix, breakpoint: Chef::Provider::Breakpoint, chef_gem: Chef::Provider::Package::Rubygems, cookbook_file: Chef::Provider::CookbookFile, csh: Chef::Provider::Script, deploy: Chef::Provider::Deploy::Timestamped, deploy_revision: Chef::Provider::Deploy::Revision, directory: Chef::Provider::Directory, dpkg_package: Chef::Provider::Package::Dpkg, dsc_script: Chef::Provider::DscScript, easy_install_package: Chef::Provider::Package::EasyInstall, erl_call: Chef::Provider::ErlCall, execute: Chef::Provider::Execute, file: Chef::Provider::File, gem_package: Chef::Provider::Package::Rubygems, git: Chef::Provider::Git, homebrew_package: Chef::Provider::Package::Homebrew, http_request: Chef::Provider::HttpRequest, ips_package: Chef::Provider::Package::Ips, link: Chef::Provider::Link, log: Chef::Provider::Log::ChefLog, macports_package: Chef::Provider::Package::Macports, mdadm: Chef::Provider::Mdadm, pacman_package: Chef::Provider::Package::Pacman, paludis_package: Chef::Provider::Package::Paludis, perl: Chef::Provider::Script, portage_package: Chef::Provider::Package::Portage, python: Chef::Provider::Script, remote_directory: Chef::Provider::RemoteDirectory, route: Chef::Provider::Route, rpm_package: Chef::Provider::Package::Rpm, ruby: Chef::Provider::Script, ruby_block: Chef::Provider::RubyBlock, script: Chef::Provider::Script, smartos_package: Chef::Provider::Package::SmartOS, solaris_package: Chef::Provider::Package::Solaris, subversion: Chef::Provider::Subversion, template: Chef::Provider::Template, timestamped_deploy: Chef::Provider::Deploy::Timestamped, whyrun_safe_ruby_block: Chef::Provider::WhyrunSafeRubyBlock, windows_package: Chef::Provider::Package::Windows, windows_service: Chef::Provider::Service::Windows, yum_package: Chef::Provider::Package::Yum, } describe "on Ubuntu 14.04" do let(:os) { "linux" } let(:platform) { "ubuntu" } let(:platform_family) { "debian" } let(:platform_version) { "14.04" } supported_providers = [ :apt_package, :bash, :breakpoint, :chef_gem, :cookbook_file, :csh, :deploy, :deploy_revision, :directory, :dpkg_package, :easy_install_package, :erl_call, :execute, :file, :gem_package, :git, :homebrew_package, :http_request, :link, :log, :macports_package, :pacman_package, :paludis_package, :perl, :python, :remote_directory, :route, :rpm_package, :ruby, :ruby_block, :script, :subversion, :template, :timestamped_deploy, :whyrun_safe_ruby_block, :yum_package, ] supported_providers.each do |static_resource| static_provider = static_mapping[static_resource] context "when the resource is a #{static_resource}" do let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) } let(:action) { :start } # in reality this doesn't matter much it "should resolve to a #{static_provider} provider" do expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup) expect(resolved_provider).to eql(static_provider) end end end unsupported_providers = [ :bff_package, :dsc_script, :ips_package, :smartos_package, :solaris_package, :windows_package, :windows_service, ] unsupported_providers.each do |static_resource| static_provider = static_mapping[static_resource] context "when the resource is a #{static_resource}" do let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) } let(:action) { :start } # in reality this doesn't matter much it "should fall back into the old provider mapper code and hooks" do retval = Object.new expect(provider_resolver).to receive(:maybe_chef_platform_lookup).and_return(retval) expect(resolved_provider).to equal(retval) end end end end end end chef-12.3.0/spec/unit/file_cache_spec.rb0000644000004100000410000000723112520074675020057 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::FileCache do before do @file_cache_path = Dir.mktmpdir Chef::Config[:file_cache_path] = @file_cache_path @io = StringIO.new end after do FileUtils.rm_rf(Chef::Config[:file_cache_path]) end describe "when the relative path to the cache file doesn't exist" do it "creates intermediate directories as needed" do Chef::FileCache.store("whiz/bang", "I found a poop") expect(File).to exist(File.join(@file_cache_path, 'whiz')) end it "creates the cached file at the correct relative path" do expect(File).to receive(:open).with(File.join(@file_cache_path, 'whiz', 'bang'), "w",416).and_yield(@io) Chef::FileCache.store("whiz/bang", "borkborkbork") end end describe "when storing a file" do before do allow(File).to receive(:open).and_yield(@io) end it "should print the contents to the file" do Chef::FileCache.store("whiz/bang", "borkborkbork") expect(@io.string).to eq("borkborkbork") end end describe "when loading cached files" do it "finds and reads the cached file" do FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) File.open(File.join(@file_cache_path, 'whiz', 'bang'), 'w') { |f| f.print("borkborkbork") } expect(Chef::FileCache.load('whiz/bang')).to eq('borkborkbork') end it "should raise a Chef::Exceptions::FileNotFound if the file doesn't exist" do expect { Chef::FileCache.load('whiz/bang') }.to raise_error(Chef::Exceptions::FileNotFound) end end describe "when deleting cached files" do before(:each) do FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) File.open(File.join(@file_cache_path, 'whiz', 'bang'), 'w') { |f| f.print("borkborkbork") } end it "unlinks the file" do Chef::FileCache.delete("whiz/bang") expect(File).not_to exist(File.join(@file_cache_path, 'whiz', 'bang')) end end describe "when listing files in the cache" do before(:each) do FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) FileUtils.touch(File.join(@file_cache_path, 'whiz', 'bang')) FileUtils.mkdir_p(File.join(@file_cache_path, 'snappy')) FileUtils.touch(File.join(@file_cache_path, 'snappy', 'patter')) end it "should return the relative paths" do expect(Chef::FileCache.list.sort).to eq(%w{snappy/patter whiz/bang}) end it "searches for cached files by globbing" do expect(Chef::FileCache.find('snappy/**/*')).to eq(%w{snappy/patter}) end end describe "when checking for the existence of a file" do before do FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) end it "has a key if the corresponding cache file exists" do FileUtils.touch(File.join(@file_cache_path, 'whiz', 'bang')) expect(Chef::FileCache).to have_key("whiz/bang") end it "doesn't have a key if the corresponding cache file doesn't exist" do expect(Chef::FileCache).not_to have_key("whiz/bang") end end end chef-12.3.0/spec/unit/user_spec.rb0000644000004100000410000002015012520074675016766 0ustar www-datawww-data# # 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 expect(@user).to be_a_kind_of(Chef::User) end end describe "name" do it "should let you set the name to a string" do expect(@user.name("ops_master")).to eq("ops_master") end it "should return the current name" do @user.name "ops_master" expect(@user.name).to eq("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 expect { @user.name "Bar" }.to raise_error(ArgumentError) # slashes expect { @user.name "foo/bar" }.to raise_error(ArgumentError) # ? expect { @user.name "foo?" }.to raise_error(ArgumentError) # & expect { @user.name "foo&" }.to raise_error(ArgumentError) end it "should not accept spaces" do expect { @user.name "ops master" }.to raise_error(ArgumentError) end it "should throw an ArgumentError if you feed it anything but a string" do expect { @user.name Hash.new }.to raise_error(ArgumentError) end end describe "admin" do it "should let you set the admin bit" do expect(@user.admin(true)).to eq(true) end it "should return the current admin value" do @user.admin true expect(@user.admin).to eq(true) end it "should default to false" do expect(@user.admin).to eq(false) end it "should throw an ArgumentError if you feed it anything but true or false" do expect { @user.name Hash.new }.to raise_error(ArgumentError) end end describe "public_key" do it "should let you set the public key" do expect(@user.public_key("super public")).to eq("super public") end it "should return the current public key" do @user.public_key("super public") expect(@user.public_key).to eq("super public") end it "should throw an ArgumentError if you feed it something lame" do expect { @user.public_key Hash.new }.to raise_error(ArgumentError) end end describe "private_key" do it "should let you set the private key" do expect(@user.private_key("super private")).to eq("super private") end it "should return the private key" do @user.private_key("super private") expect(@user.private_key).to eq("super private") end it "should throw an ArgumentError if you feed it something lame" do expect { @user.private_key Hash.new }.to 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 expect(@json).to match(/^\{.+\}$/) end it "includes the name value" do expect(@json).to include(%q{"name":"black"}) end it "includes the public key value" do expect(@json).to include(%{"public_key":"crowes"}) end it "includes the 'admin' flag" do expect(@json).to include(%q{"admin":false}) end it "includes the private key when present" do @user.private_key("monkeypants") expect(@user.to_json).to include(%q{"private_key":"monkeypants"}) end it "does not include the private key if not present" do expect(@json).not_to include("private_key") end it "includes the password if present" do @user.password "password" expect(@user.to_json).to include(%q{"password":"password"}) end it "does not include the password if not present" do expect(@json).not_to include("password") end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @user } 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(Chef::JSONCompat.to_json(user)) end it "should deserialize to a Chef::User object" do expect(@user).to be_a_kind_of(Chef::User) end it "preserves the name" do expect(@user.name).to eq("mr_spinks") end it "preserves the public key" do expect(@user.public_key).to eq("turtles") end it "preserves the admin status" do expect(@user.admin).to be_truthy end it "includes the private key if present" do expect(@user.private_key).to eq("pandas") end it "includes the password if present" do expect(@user.password).to eq("password") end end describe "API Interactions" do before (:each) do @user = Chef::User.new @user.name "foobar" @http_client = double("Chef::REST mock") allow(Chef::REST).to receive(: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" }} ] allow(Chef::User).to receive(:load).with("admin").and_return(@user) @osc_inflated_response = { "admin" => @user } end it "lists all clients on an OSC server" do allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response) expect(Chef::User.list).to eq(@osc_response) end it "inflate all clients on an OSC server" do allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response) expect(Chef::User.list(true)).to eq(@osc_inflated_response) end it "lists all clients on an OHC/OPC server" do allow(@http_client).to receive(: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. expect(Chef::User.list).to eq(@osc_response) end it "inflate all clients on an OHC/OPC server" do allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response) expect(Chef::User.list(true)).to eq(@osc_inflated_response) end end describe "create" do it "creates a new user via the API" do @user.password "password" expect(@http_client).to 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 expect(@http_client).to receive(:get_rest).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"}) user = Chef::User.load("foobar") expect(user.name).to eq("foobar") expect(user.admin).to eq(true) expect(user.public_key).to eq("pubkey") end end describe "update" do it "updates an existing user on via the API" do expect(@http_client).to 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 expect(@http_client).to receive(:delete_rest).with("users/foobar") @user.destroy end end end end chef-12.3.0/spec/unit/monologger_spec.rb0000644000004100000410000000303512520074675020163 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 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/monologger' require 'tempfile' require 'spec_helper' describe MonoLogger do it "should disable buffering when passed an IO stream" do STDOUT.sync = false MonoLogger.new(STDOUT) expect(STDOUT.sync).to eq(true) end describe "when given an object that responds to write and close e.g. IO" do it "should use the object directly" do stream = StringIO.new MonoLogger.new(stream).fatal("Houston, we've had a problem.") expect(stream.string).to match(/Houston, we've had a problem./) end end describe "when given an object that is stringable (to_str)" do it "should open a File object with the given path" do temp_file = Tempfile.new("rspec-monologger-log") temp_file.close MonoLogger.new(temp_file.path).fatal("Do, or do not. There is no try.") expect(File.read(temp_file.path)).to match(/Do, or do not. There is no try./) end end end chef-12.3.0/spec/unit/recipe_spec.rb0000644000004100000410000005415612520074675017274 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Author:: Seth Chisamore () # Copyright:: Copyright (c) 2008-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 'spec_helper' require 'chef/platform/resource_priority_map' describe Chef::Recipe do let(:cookbook_repo) { File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks")) } let(:cookbook_loader) do loader = Chef::CookbookLoader.new(cookbook_repo) loader.load_cookbooks loader end let(:cookbook_collection) { Chef::CookbookCollection.new(cookbook_loader) } let(:node) do Chef::Node.new.tap {|n| n.normal[:tags] = [] } end let(:events) do Chef::EventDispatch::Dispatcher.new end let(:run_context) do Chef::RunContext.new(node, cookbook_collection, events) end let(:recipe) do Chef::Recipe.new("hjk", "test", run_context) end describe "method_missing" do describe "resources" do it "should load a two word (zen_master) resource" do expect do recipe.zen_master "monkey" do peace true end end.not_to raise_error end it "should load a one word (cat) resource" do expect do recipe.cat "loulou" do pretty_kitty true end end.not_to raise_error end it "should load a four word (one_two_three_four) resource" do expect do recipe.one_two_three_four "numbers" do i_can_count true end end.not_to raise_error end it "should throw an error if you access a resource that we can't find" do expect { recipe.not_home("not_home_resource") }.to raise_error(NameError) end it "should require a name argument" do expect { recipe.cat }.to raise_error(ArgumentError, "You must supply a name when declaring a cat resource") end it "should allow regular errors (not NameErrors) to pass unchanged" do expect { recipe.cat("felix") { raise ArgumentError, "You Suck" } }.to raise_error(ArgumentError) end it "should add our zen_master to the collection" do recipe.zen_master "monkey" do peace true end expect(run_context.resource_collection.lookup("zen_master[monkey]").name).to eql("monkey") end it "should add our zen masters to the collection in the order they appear" do %w{monkey dog cat}.each do |name| recipe.zen_master name do peace true end end expect(run_context.resource_collection.map{|r| r.name}).to eql(["monkey", "dog", "cat"]) end it "should return the new resource after creating it" do res = recipe.zen_master "makoto" do peace true end expect(res.resource_name).to eql(:zen_master) expect(res.name).to eql("makoto") end describe "should locate platform mapped resources" do it "locate resource for particular platform" do ShaunTheSheep = Class.new(Chef::Resource) ShaunTheSheep.provides :laughter, :on_platforms => ["television"] node.automatic[:platform] = "television" node.automatic[:platform_version] = "123" res = recipe.laughter "timmy" expect(res.name).to eql("timmy") res.kind_of?(ShaunTheSheep) end it "locate a resource for all platforms" do YourMom = Class.new(Chef::Resource) YourMom.provides :love_and_caring res = recipe.love_and_caring "mommy" expect(res.name).to eql("mommy") res.kind_of?(YourMom) end describe "when there is more than one resource that resolves on a node" do before do node.automatic[:platform] = "nbc_sports" Sounders = Class.new(Chef::Resource) Sounders.provides :football, platform: "nbc_sports" TottenhamHotspur = Class.new(Chef::Resource) TottenhamHotspur.provides :football, platform: "nbc_sports" end after do Object.send(:remove_const, :Sounders) Object.send(:remove_const, :TottenhamHotspur) end it "warns if resolution of the two resources is ambiguous" do expect(Chef::Log).to receive(:warn).at_least(:once).with(/Ambiguous resource precedence/) res1 = recipe.football "club world cup" expect(res1.name).to eql("club world cup") # the class of res1 is not defined. end it "selects one if it is given priority" do expect(Chef::Log).not_to receive(:warn) Chef::Platform::ResourcePriorityMap.instance.send(:priority, :football, TottenhamHotspur, platform: "nbc_sports") res1 = recipe.football "club world cup" expect(res1.name).to eql("club world cup") expect(res1).to be_a_kind_of(TottenhamHotspur) end it "selects the other one if it is given priority" do expect(Chef::Log).not_to receive(:warn) Chef::Platform::ResourcePriorityMap.instance.send(:priority, :football, Sounders, platform: "nbc_sports") res1 = recipe.football "club world cup" expect(res1.name).to eql("club world cup") expect(res1).to be_a_kind_of(Sounders) end end end end describe "creating resources via build_resource" do let(:zm_resource) do recipe.build_resource(:zen_master, "klopp") do something "bvb" end end it "applies attributes from the block to the resource" do expect(zm_resource.something).to eq("bvb") end it "sets contextual attributes on the resource" do expect(zm_resource.recipe_name).to eq("test") expect(zm_resource.cookbook_name).to eq("hjk") expect(zm_resource.source_line).to include(__FILE__) expect(zm_resource.declared_type).to eq(:zen_master) end it "does not add the resource to the resource collection" do zm_resource # force let binding evaluation expect { run_context.resource_collection.resources(:zen_master => "klopp") }.to raise_error(Chef::Exceptions::ResourceNotFound) end end describe "when cloning resources" do def expect_warning expect(Chef::Log).to receive(:warn).with(/3694/) expect(Chef::Log).to receive(:warn).with(/Previous/) expect(Chef::Log).to receive(:warn).with(/Current/) end it "should emit a 3694 warning when attributes change" do recipe.zen_master "klopp" do something "bvb" end expect_warning recipe.zen_master "klopp" do something "vbv" end end it "should emit a 3694 warning when attributes change" do recipe.zen_master "klopp" do something "bvb" end expect_warning recipe.zen_master "klopp" do something "bvb" peace true end end it "should emit a 3694 warning when attributes change" do recipe.zen_master "klopp" do something "bvb" peace true end expect_warning recipe.zen_master "klopp" do something "bvb" end end it "should emit a 3694 warning for non-trivial attributes (unfortunately)" do recipe.zen_master "klopp" do something "bvb" end expect_warning recipe.zen_master "klopp" do something "bvb" end end it "should not emit a 3694 warning for completely trivial resource cloning" do recipe.zen_master "klopp" expect(Chef::Log).to_not receive(:warn) recipe.zen_master "klopp" end it "should not emit a 3694 warning when attributes do not change and the first action is :nothing" do recipe.zen_master "klopp" do action :nothing end expect(Chef::Log).to_not receive(:warn) recipe.zen_master "klopp" do action :score end end it "should not emit a 3694 warning when attributes do not change and the second action is :nothing" do recipe.zen_master "klopp" do action :score end expect(Chef::Log).to_not receive(:warn) recipe.zen_master "klopp" do action :nothing end end it "validating resources via build_resource" do expect {recipe.build_resource(:remote_file, "klopp") do source Chef::DelayedEvaluator.new {"http://chef.io"} end}.to_not raise_error end end describe "creating resources via declare_resource" do let(:zm_resource) do recipe.declare_resource(:zen_master, "klopp") do something "bvb" end end it "applies attributes from the block to the resource" do expect(zm_resource.something).to eq("bvb") end it "sets contextual attributes on the resource" do expect(zm_resource.recipe_name).to eq("test") expect(zm_resource.cookbook_name).to eq("hjk") expect(zm_resource.source_line).to include(__FILE__) end it "adds the resource to the resource collection" do zm_resource # force let binding evaluation expect(run_context.resource_collection.resources(:zen_master => "klopp")).to eq(zm_resource) end end describe "creating a resource with short name" do # zen_follower resource has this: # provides :follower, :on_platforms => ["zen"] before do node.automatic_attrs[:platform] = "zen" end let(:resource_follower) do recipe.declare_resource(:follower, "srst") do master "none" end end it "defines the resource using the declaration name with short name" do resource_follower expect(run_context.resource_collection.lookup("follower[srst]")).not_to be_nil end end describe "creating a resource with a long name" do let(:resource_zn_follower) do recipe.declare_resource(:zen_follower, "srst") do master "none" end end it "defines the resource using the declaration name with long name" do resource_zn_follower expect(run_context.resource_collection.lookup("zen_follower[srst]")).not_to be_nil end end describe "when attempting to create a resource of an invalid type" do it "gives a sane error message when using method_missing" do expect do recipe.no_such_resource("foo") end.to raise_error(NoMethodError, %q[No resource or method named `no_such_resource' for `Chef::Recipe "test"']) end it "gives a sane error message when using method_missing 'bare'" do expect do recipe.instance_eval do # Giving an argument will change this from NameError to NoMethodError no_such_resource end end.to raise_error(NameError, %q[No resource, method, or local variable named `no_such_resource' for `Chef::Recipe "test"']) end it "gives a sane error message when using build_resource" do expect { recipe.build_resource(:no_such_resource, "foo") }.to raise_error(Chef::Exceptions::NoSuchResourceType) end it "gives a sane error message when using declare_resource" do expect { recipe.declare_resource(:no_such_resource, "bar") }.to raise_error(Chef::Exceptions::NoSuchResourceType) end end describe "when creating a resource that contains an error in the attributes block" do it "does not obfuscate the error source" do expect do recipe.zen_master("klopp") do this_method_doesnt_exist end end.to raise_error(NoMethodError, "undefined method `this_method_doesnt_exist' for Chef::Resource::ZenMaster") end end describe "resource cloning" do let(:second_recipe) do Chef::Recipe.new("second_cb", "second_recipe", run_context) end let(:original_resource) do recipe.zen_master("klopp") do something "bvb09" action :score end end let(:duplicated_resource) do original_resource second_recipe.zen_master("klopp") do # attrs should be cloned end end it "copies attributes from the first resource" do expect(duplicated_resource.something).to eq("bvb09") end it "does not copy the action from the first resource" do expect(original_resource.action).to eq([:score]) expect(duplicated_resource.action).to eq(:nothing) end it "does not copy the source location of the first resource" do # sanity check source location: expect(original_resource.source_line).to include(__FILE__) expect(duplicated_resource.source_line).to include(__FILE__) # actual test: expect(original_resource.source_line).not_to eq(duplicated_resource.source_line) end it "sets the cookbook name on the cloned resource to that resource's cookbook" do expect(duplicated_resource.cookbook_name).to eq("second_cb") end it "sets the recipe name on the cloned resource to that resoure's recipe" do expect(duplicated_resource.recipe_name).to eq("second_recipe") end end describe "resource definitions" do it "should execute defined resources" do crow_define = Chef::ResourceDefinition.new crow_define.define :crow, :peace => false, :something => true do zen_master "lao tzu" do peace params[:peace] something params[:something] end end run_context.definitions[:crow] = crow_define recipe.crow "mine" do peace true end expect(run_context.resource_collection.resources(:zen_master => "lao tzu").name).to eql("lao tzu") expect(run_context.resource_collection.resources(:zen_master => "lao tzu").something).to eql(true) end it "should set the node on defined resources" do crow_define = Chef::ResourceDefinition.new crow_define.define :crow, :peace => false, :something => true do zen_master "lao tzu" do peace params[:peace] something params[:something] end end run_context.definitions[:crow] = crow_define node.normal[:foo] = false recipe.crow "mine" do something node[:foo] end expect(recipe.resources(:zen_master => "lao tzu").something).to eql(false) end it "should return the last statement in the definition as the retval" do crow_define = Chef::ResourceDefinition.new crow_define.define :crow, :peace => false, :something => true do "the return val" end run_context.definitions[:crow] = crow_define crow_block = recipe.crow "mine" do peace true end expect(crow_block).to eql("the return val") end end end describe "instance_eval" do it "should handle an instance_eval properly" do code = <<-CODE zen_master "gnome" do peace = true end CODE expect { recipe.instance_eval(code) }.not_to raise_error expect(recipe.resources(:zen_master => "gnome").name).to eql("gnome") end end describe "handle exec calls" do it "should raise ResourceNotFound error if exec is used" do code = <<-CODE exec 'do_not_try_to_exec' CODE expect { recipe.instance_eval(code) }.to raise_error(Chef::Exceptions::ResourceNotFound) end end describe "from_file" do it "should load a resource from a ruby file" do recipe.from_file(File.join(CHEF_SPEC_DATA, "recipes", "test.rb")) res = recipe.resources(:file => "/etc/nsswitch.conf") expect(res.name).to eql("/etc/nsswitch.conf") expect(res.action).to eql([:create]) expect(res.owner).to eql("root") expect(res.group).to eql("root") expect(res.mode).to eql(0644) end it "should raise an exception if the file cannot be found or read" do expect { recipe.from_file("/tmp/monkeydiving") }.to raise_error(IOError) end end describe "include_recipe" do it "should evaluate another recipe with include_recipe" do expect(node).to receive(:loaded_recipe).with(:openldap, "gigantor") allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) run_context.include_recipe "openldap::gigantor" res = run_context.resource_collection.resources(:cat => "blanket") expect(res.name).to eql("blanket") expect(res.pretty_kitty).to eql(false) end it "should load the default recipe for a cookbook if include_recipe is called without a ::" do expect(node).to receive(:loaded_recipe).with(:openldap, "default") allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) run_context.include_recipe "openldap" res = run_context.resource_collection.resources(:cat => "blanket") expect(res.name).to eql("blanket") expect(res.pretty_kitty).to eql(true) end it "should store that it has seen a recipe in the run_context" do expect(node).to receive(:loaded_recipe).with(:openldap, "default") allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) run_context.include_recipe "openldap" expect(run_context.loaded_recipe?("openldap")).to be_truthy end it "should not include the same recipe twice" do expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once) allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context) recipe.include_recipe "openldap" expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context) recipe.include_recipe "openldap" end it "will load a recipe out of the current cookbook when include_recipe is called with a leading ::" do openldap_recipe = Chef::Recipe.new("openldap", "test", run_context) expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once) allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context) openldap_recipe.include_recipe "::default" end it "will not include the same recipe twice when using leading :: syntax" do openldap_recipe = Chef::Recipe.new("openldap", "test", run_context) expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once) allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context) openldap_recipe.include_recipe "::default" expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context) openldap_recipe.include_recipe "openldap::default" end it "will not include the same recipe twice when using leading :: syntax (reversed order)" do openldap_recipe = Chef::Recipe.new("openldap", "test", run_context) expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once) allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false) expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context) openldap_recipe.include_recipe "openldap::default" expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context) openldap_recipe.include_recipe "::default" end end describe "tags" do describe "with the default node object" do let(:node) { Chef::Node.new } it "should return false for any tags" do expect(recipe.tagged?("foo")).to be(false) end end it "should set tags via tag" do recipe.tag "foo" expect(node[:tags]).to include("foo") end it "should set multiple tags via tag" do recipe.tag "foo", "bar" expect(node[:tags]).to include("foo") expect(node[:tags]).to include("bar") end it "should not set the same tag twice via tag" do recipe.tag "foo" recipe.tag "foo" expect(node[:tags]).to eql([ "foo" ]) end it "should return the current list of tags from tag with no arguments" do recipe.tag "foo" expect(recipe.tag).to eql([ "foo" ]) end it "should return true from tagged? if node is tagged" do recipe.tag "foo" expect(recipe.tagged?("foo")).to be(true) end it "should return false from tagged? if node is not tagged" do expect(recipe.tagged?("foo")).to be(false) end it "should return false from tagged? if node is not tagged" do expect(recipe.tagged?("foo")).to be(false) end it "should remove a tag from the tag list via untag" do recipe.tag "foo" recipe.untag "foo" expect(node[:tags]).to eql([]) end it "should remove multiple tags from the tag list via untag" do recipe.tag "foo", "bar" recipe.untag "bar", "foo" expect(node[:tags]).to eql([]) end end describe "included DSL" do it "should include features from Chef::DSL::Audit" do expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit) expect(recipe.respond_to?(:control_group)).to be true end it "should respond to :ps_credential from Chef::DSL::Powershell" do expect(recipe.respond_to?(:ps_credential)).to be true end end end chef-12.3.0/spec/unit/registry_helper_spec.rb0000644000004100000410000004652112520074675021231 0ustar www-datawww-data# # Author:: Prajakta Purohit (prajakta@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::Provider::RegistryKey do let(:value1) { { :name => "one", :type => :string, :data => "1" } } let(:key_path) { 'HKCU\Software\OpscodeNumbers' } let(:key) { 'Software\OpscodeNumbers' } let(:key_parent) { 'Software' } let(:key_to_delete) { 'OpscodeNumbers' } let(:sub_key) {'OpscodePrimes'} let(:missing_key_path) {'HKCU\Software'} before(:each) do allow_any_instance_of(Chef::Win32::Registry).to receive(:machine_architecture).and_return(:x86_64) @registry = Chef::Win32::Registry.new() #Making the values for registry constants available on unix Object.send(:remove_const, 'Win32') if defined?(Win32) Win32 = Module.new Win32::Registry = Class.new Win32::Registry::KEY_SET_VALUE = 0x0002 Win32::Registry::KEY_QUERY_VALUE = 0x0001 Win32::Registry::KEY_WRITE = 0x00020000 | 0x0002 | 0x0004 Win32::Registry::KEY_READ = 0x00020000 | 0x0001 | 0x0008 | 0x0010 Win32::Registry::Error = Class.new(RuntimeError) @hive_mock = double("::Win32::Registry::HKEY_CURRENT_USER") @reg_mock = double("reg") end describe "get_values" do it "gets all values for a key if the key exists" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:map) @registry.get_values(key_path) end it "throws an exception if key does not exist" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) expect{@registry.get_values(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "set_value" do it "does nothing if key and hive and value exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true) expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true) @registry.set_value(key_path, value1) end it "updates value if key and hive and value exist, but data is different" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true) expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(false) expect(@hive_mock).to receive(:open).with(key, Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1) expect(@reg_mock).to receive(:write).with("one", 1, "1") @registry.set_value(key_path, value1) end it "creates value if the key exists and the value does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1) expect(@reg_mock).to receive(:write).with("one", 1, "1") @registry.set_value(key_path, value1) end it "should raise an exception if the key does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) expect {@registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "delete_value" do it "deletes value if value exists" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:delete_value).with("one").and_return(true) @registry.delete_value(key_path, value1) end it "raises an exception if the key does not exist" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) @registry.delete_value(key_path, value1) end it "does nothing if the value does not exist" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false) @registry.delete_value(key_path, value1) end end describe "create_key" do it "creates key if intermediate keys are missing and recursive is set to true" do expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true) expect(@registry).to receive(:create_missing).with(key_path) expect(@registry).to receive(:key_exists?).with(key_path).and_return(false) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture) @registry.create_key(key_path, true) end it "raises an exception if intermediate keys are missing and recursive is set to false" do expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true) expect{@registry.create_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) end it "does nothing if the key exists" do expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true) expect(@registry).to receive(:create_missing).with(key_path) expect(@registry).to receive(:key_exists?).with(key_path).and_return(true) @registry.create_key(key_path, true) end it "create key if intermediate keys not missing and recursive is set to false" do expect(@registry).to receive(:keys_missing?).with(key_path).and_return(false) expect(@registry).to receive(:key_exists?).with(key_path).and_return(false) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture) @registry.create_key(key_path, false) end it "create key if intermediate keys not missing and recursive is set to true" do expect(@registry).to receive(:keys_missing?).with(key_path).and_return(false) expect(@registry).to receive(:key_exists?).with(key_path).and_return(false) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture) @registry.create_key(key_path, true) end end describe "delete_key", :windows_only do it "deletes key if it has subkeys and recursive is set to true" do expect(@registry).to receive(:key_exists?).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(true) expect(@registry).to receive(:get_subkeys).with(key_path).and_return([sub_key]) expect(@registry).to receive(:key_exists?).with(key_path+"\\"+sub_key).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path+"\\"+sub_key).and_return([@hive_mock, key+"\\"+sub_key]) expect(@registry).to receive(:has_subkeys?).with(key_path+"\\"+sub_key).and_return(false) expect(@registry).to receive(:delete_key_ex).twice @registry.delete_key(key_path, true) end it "raises an exception if it has subkeys but recursive is set to false" do expect(@registry).to receive(:key_exists?).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(true) expect{@registry.delete_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) end it "deletes key if the key exists and has no subkeys" do expect(@registry).to receive(:key_exists?).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(false) expect(@registry).to receive(:delete_key_ex) @registry.delete_key(key_path, true) end end describe "key_exists?" do it "returns true if key_exists" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry.key_exists?(key_path)).to eq(true) end it "returns false if key does not exist" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_raise(::Win32::Registry::Error) expect(@registry.key_exists?(key_path)).to eq(false) end end describe "key_exists!" do it "throws an exception if the key_parent does not exist" do expect(@registry).to receive(:key_exists?).with(key_path).and_return(false) expect{@registry.key_exists!(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "hive_exists?" do it "returns true if the hive exists" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) @registry.hive_exists?(key_path) == true end it "returns false if the hive does not exist" do expect(@registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegHiveMissing) @registry.hive_exists?(key_path) == false end end describe "has_subkeys?" do it "returns true if the key has subkeys" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:each_key).and_yield(key) @registry.has_subkeys?(key_path) == true end it "returns false if the key does not have subkeys" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:each_key).and_return(no_args()) expect(@registry.has_subkeys?(key_path)).to eq(false) end it "throws an exception if the key does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) expect {@registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "get_subkeys" do it "returns the subkeys if they exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:each_key).and_yield(sub_key) @registry.get_subkeys(key_path) end end describe "value_exists?" do it "throws an exception if the key does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) expect {@registry.value_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if the value exists" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:any?).and_yield("one") @registry.value_exists?(key_path, value1) == true end it "returns false if the value does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:any?).and_yield(no_args()) @registry.value_exists?(key_path, value1) == false end end describe "data_exists?" do it "throws an exception if the key does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing) expect {@registry.data_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if the data exists" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1) expect(@reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "1") expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry.data_exists?(key_path, value1)).to eq(true) end it "returns false if the data does not exist" do expect(@registry).to receive(:key_exists!).with(key_path).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1) expect(@reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "2") expect(@registry.data_exists?(key_path, value1)).to eq(false) end end describe "value_exists!" do it "does nothing if the value exists" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true) @registry.value_exists!(key_path, value1) end it "throws an exception if the value does not exist" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false) expect{@registry.value_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing) end end describe "data_exists!" do it "does nothing if the data exists" do expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true) @registry.data_exists!(key_path, value1) end it "throws an exception if the data does not exist" do expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(false) expect{@registry.data_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegDataMissing) end end describe "type_matches?" do it "returns true if type matches" do expect(@registry).to receive(:value_exists!).with(key_path, value1).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1) expect(@reg_mock).to receive(:each).and_yield("one", 1) expect(@registry.type_matches?(key_path, value1)).to eq(true) end it "returns false if type does not match" do expect(@registry).to receive(:value_exists!).with(key_path, value1).and_return(true) expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key]) expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock) expect(@reg_mock).to receive(:each).and_yield("two", 2) expect(@registry.type_matches?(key_path, value1)).to eq(false) end it "throws an exception if value does not exist" do expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false) expect{@registry.type_matches?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing) end end describe "type_matches!" do it "does nothing if the type_matches" do expect(@registry).to receive(:type_matches?).with(key_path, value1).and_return(true) @registry.type_matches!(key_path, value1) end it "throws an exception if the type does not match" do expect(@registry).to receive(:type_matches?).with(key_path, value1).and_return(false) expect{@registry.type_matches!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegTypesMismatch) end end describe "keys_missing?" do it "returns true if the keys are missing" do expect(@registry).to receive(:key_exists?).with(missing_key_path).and_return(false) expect(@registry.keys_missing?(key_path)).to eq(true) end it "returns false if no keys in the path are missing" do expect(@registry).to receive(:key_exists?).with(missing_key_path).and_return(true) expect(@registry.keys_missing?(key_path)).to eq(false) end end end chef-12.3.0/spec/unit/resource_collection_spec.rb0000644000004100000410000002234612520074675022063 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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 'spec_helper' describe Chef::ResourceCollection do let(:rc) { Chef::ResourceCollection.new() } let(:resource) { Chef::Resource::ZenMaster.new("makoto") } it "should throw an error when calling a non-delegated method" do expect { rc.not_a_method }.to raise_error(NoMethodError) end describe "initialize" do it "should return a Chef::ResourceCollection" do expect(rc).to be_kind_of(Chef::ResourceCollection) end end describe "[]" do it "should accept Chef::Resources through [index]" do expect { rc[0] = resource }.not_to raise_error expect { rc[0] = "string" }.to raise_error(ArgumentError) end it "should allow you to fetch Chef::Resources by position" do rc[0] = resource expect(rc[0]).to eql(resource) end end describe "push" do it "should accept Chef::Resources through pushing" do expect { rc.push(resource) }.not_to raise_error expect { rc.push("string") }.to raise_error(ArgumentError) end end describe "<<" do it "should accept the << operator" do expect { rc << resource }.not_to raise_error end end describe "insert" do it "should accept only Chef::Resources" do expect { rc.insert(resource) }.not_to raise_error expect { rc.insert("string") }.to raise_error(ArgumentError) end it "should accept named arguments in any order" do rc.insert(resource, :instance_name => 'foo', :resource_type =>'bar') expect(rc[0]).to eq(resource) end it "should append resources to the end of the collection when not executing a run" do zmr = Chef::Resource::ZenMaster.new("there is no spoon") rc.insert(resource) rc.insert(zmr) expect(rc[0]).to eql(resource) expect(rc[1]).to eql(zmr) end it "should insert resources to the middle of the collection if called while executing a run" do resource_to_inject = Chef::Resource::ZenMaster.new("there is no spoon") zmr = Chef::Resource::ZenMaster.new("morpheus") dummy = Chef::Resource::ZenMaster.new("keanu reeves") rc.insert(zmr) rc.insert(dummy) rc.execute_each_resource do |resource| rc.insert(resource_to_inject) if resource == zmr end expect(rc[0]).to eql(zmr) expect(rc[1]).to eql(resource_to_inject) expect(rc[2]).to eql(dummy) end end describe "each" do it "should allow you to iterate over every resource in the collection" do load_up_resources results = Array.new expect { rc.each do |r| results << r.name end }.not_to raise_error results.each_index do |i| case i when 0 expect(results[i]).to eql("dog") when 1 expect(results[i]).to eql("cat") when 2 expect(results[i]).to eql("monkey") end end end end describe "each_index" do it "should allow you to iterate over every resource by index" do load_up_resources results = Array.new expect { rc.each_index do |i| results << rc[i].name end }.not_to raise_error results.each_index do |i| case i when 0 expect(results[i]).to eql("dog") when 1 expect(results[i]).to eql("cat") when 2 expect(results[i]).to eql("monkey") end end end end describe "lookup" do it "should allow you to find resources by name via lookup" do zmr = Chef::Resource::ZenMaster.new("dog") rc << zmr expect(rc.lookup(zmr.to_s)).to eql(zmr) zmr = Chef::Resource::ZenMaster.new("cat") rc[0] = zmr expect(rc.lookup(zmr)).to eql(zmr) zmr = Chef::Resource::ZenMaster.new("monkey") rc.push(zmr) expect(rc.lookup(zmr)).to eql(zmr) end it "should raise an exception if you send something strange to lookup" do expect { rc.lookup(:symbol) }.to raise_error(ArgumentError) end it "should raise an exception if it cannot find a resource with lookup" do expect { rc.lookup("zen_master[dog]") }.to raise_error(Chef::Exceptions::ResourceNotFound) end end describe "resources" do it "should find a resource by symbol and name (:zen_master => monkey)" do load_up_resources expect(rc.resources(:zen_master => "monkey").name).to eql("monkey") end it "should find a resource by symbol and array of names (:zen_master => [a,b])" do load_up_resources results = rc.resources(:zen_master => [ "monkey", "dog" ]) expect(results.length).to eql(2) check_by_names(results, "monkey", "dog") end it "should find resources of multiple kinds (:zen_master => a, :file => b)" do load_up_resources results = rc.resources(:zen_master => "monkey", :file => "something") expect(results.length).to eql(2) check_by_names(results, "monkey", "something") end it "should find a resource by string zen_master[a]" do load_up_resources expect(rc.resources("zen_master[monkey]").name).to eql("monkey") end it "should find resources by strings of zen_master[a,b]" do load_up_resources results = rc.resources("zen_master[monkey,dog]") expect(results.length).to eql(2) check_by_names(results, "monkey", "dog") end it "should find resources of multiple types by strings of zen_master[a]" do load_up_resources results = rc.resources("zen_master[monkey]", "file[something]") expect(results.length).to eql(2) check_by_names(results, "monkey", "something") end it "should raise an exception if you pass a bad name to resources" do expect { rc.resources("michael jackson") }.to raise_error(ArgumentError) end it "should raise an exception if you pass something other than a string or hash to resource" do expect { rc.resources([Array.new]) }.to raise_error(ArgumentError) end it "raises an error when attempting to find a resource that does not exist" do expect {rc.find("script[nonesuch]")}.to raise_error(Chef::Exceptions::ResourceNotFound) end end describe "when validating a resource query object" do it "accepts a string of the form 'resource_type[resource_name]'" do expect(rc.validate_lookup_spec!("resource_type[resource_name]")).to be_truthy end it "accepts a single-element :resource_type => 'resource_name' Hash" do expect(rc.validate_lookup_spec!(:service => "apache2")).to be_truthy end it "accepts a chef resource object" do res = Chef::Resource.new("foo", nil) expect(rc.validate_lookup_spec!(res)).to be_truthy end it "rejects a malformed query string" do expect do rc.validate_lookup_spec!("resource_type[missing-end-bracket") end.to raise_error(Chef::Exceptions::InvalidResourceSpecification) end it "rejects an argument that is not a String, Hash, or Chef::Resource" do expect do rc.validate_lookup_spec!(Object.new) end.to raise_error(Chef::Exceptions::InvalidResourceSpecification) end end describe "to_json" do it "should serialize to json" do json = rc.to_json expect(json).to match(/json_class/) expect(json).to match(/instance_vars/) end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { rc } end end describe "self.from_json" do it "should not respond to this method" do expect(rc.respond_to?(:from_json)).to eq(false) end it "should convert from json using the CHEF::JSONCompat library" do rc << resource json = Chef::JSONCompat.to_json(rc) s_rc = Chef::JSONCompat.from_json(json) expect(s_rc).to be_a_kind_of(Chef::ResourceCollection) expect(s_rc[0].name).to eql(resource.name) end end describe "provides access to the raw resources array" do it "returns the resources via the all_resources method" do expect(rc.all_resources).to equal(rc.instance_variable_get(:@resource_list).instance_variable_get(:@resources)) end end describe "provides access to stepable iterator" do it "returns the iterator object" do rc.instance_variable_get(:@resource_list).instance_variable_set(:@iterator, :fooboar) expect(rc.iterator).to eq(:fooboar) end end def check_by_names(results, *names) names.each do |res_name| expect(results.detect{ |res| res.name == res_name }).not_to eql(nil) end end def load_up_resources %w{dog cat monkey}.each do |n| rc << Chef::Resource::ZenMaster.new(n) end rc << Chef::Resource::File.new("something") end end chef-12.3.0/spec/unit/resource_definition_spec.rb0000644000004100000410000000644212520074675022057 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::ResourceDefinition do let(:defn) { Chef::ResourceDefinition.new() } describe "initialize" do it "should be a Chef::ResourceDefinition" do expect(defn).to be_a_kind_of(Chef::ResourceDefinition) end it "should not initialize a new node if one is not provided" do expect(defn.node).to eql(nil) end it "should accept a node as an argument" do node = Chef::Node.new node.name("bobo") defn = Chef::ResourceDefinition.new(node) expect(defn.node.name).to eq("bobo") end end describe "node" do it "should set the node with node=" do node = Chef::Node.new node.name("bobo") defn.node = node expect(defn.node.name).to eq("bobo") end it "should return the node" do defn.node = Chef::Node.new expect(defn.node).to be_a_kind_of(Chef::Node) end end it "should accept a new definition with a symbol for a name" do expect { defn.define :smoke do end }.not_to raise_error expect { defn.define "george washington" do end }.to raise_error(ArgumentError) expect(defn.name).to eql(:smoke) end it "should accept a new definition with a hash" do expect { defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do end }.not_to raise_error end it "should expose the prototype hash params in the params hash" do defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do; end expect(defn.params[:cigar]).to eql("cuban") expect(defn.params[:cigarette]).to eql("marlboro") end it "should store the block passed to define as a proc under recipe" do defn.define :smoke do "I am what I am" end expect(defn.recipe).to be_a_kind_of(Proc) expect(defn.recipe.call).to eql("I am what I am") end it "should set parameters based on method_missing" do defn.mind "to fly" expect(defn.params[:mind]).to eql("to fly") end it "should raise an exception if prototype_params is not a hash" do expect { defn.define :monkey, Array.new do end }.to raise_error(ArgumentError) end it "should raise an exception if define is called without a block" do expect { defn.define :monkey }.to raise_error(ArgumentError) end it "should load a description from a file" do defn.from_file(File.join(CHEF_SPEC_DATA, "definitions", "test.rb")) expect(defn.name).to eql(:rico_suave) expect(defn.params[:rich]).to eql("smooth") end it "should turn itself into a string based on the name with to_s" do defn.name = :woot expect(defn.to_s).to eql("woot") end end chef-12.3.0/spec/unit/version_constraint_spec.rb0000644000004100000410000001305112520074675021743 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010 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/version_constraint' describe Chef::VersionConstraint do describe "validation" do bad_version = [">= 1.2.z", "> 1.2.3 < 5.0", "> 1.2.3, < 5.0"] bad_op = ["> >", ">$ 1.2.3", "! 3.4"] o_error = Chef::Exceptions::InvalidVersionConstraint v_error = Chef::Exceptions::InvalidCookbookVersion bad_version.each do |s| it "should raise #{v_error} when given #{s}" do expect { Chef::VersionConstraint.new s }.to raise_error(v_error) end end bad_op.each do |s| it "should raise #{o_error} when given #{s}" do expect { Chef::VersionConstraint.new s }.to raise_error(o_error) end end it "should interpret a lone version number as implicit = OP" do vc = Chef::VersionConstraint.new("1.2.3") expect(vc.to_s).to eq("= 1.2.3") end it "should allow initialization with [] for back compatibility" do Chef::VersionConstraint.new([]) == Chef::VersionConstraint.new end it "should allow initialization with ['1.2.3'] for back compatibility" do Chef::VersionConstraint.new(["1.2"]) == Chef::VersionConstraint.new("1.2") end end it "should default to >= 0.0.0" do vc = Chef::VersionConstraint.new expect(vc.to_s).to eq(">= 0.0.0") end it "should default to >= 0.0.0 when initialized with nil" do expect(Chef::VersionConstraint.new(nil).to_s).to eq(">= 0.0.0") end it "should work with Chef::Version classes" do vc = Chef::VersionConstraint.new("1.0") expect(vc.version).to be_an_instance_of(Chef::Version) end it "should allow ops without space separator" do expect(Chef::VersionConstraint.new("=1.2.3")).to eql(Chef::VersionConstraint.new("= 1.2.3")) expect(Chef::VersionConstraint.new(">1.2.3")).to eql(Chef::VersionConstraint.new("> 1.2.3")) expect(Chef::VersionConstraint.new("<1.2.3")).to eql(Chef::VersionConstraint.new("< 1.2.3")) expect(Chef::VersionConstraint.new(">=1.2.3")).to eql(Chef::VersionConstraint.new(">= 1.2.3")) expect(Chef::VersionConstraint.new("<=1.2.3")).to eql(Chef::VersionConstraint.new("<= 1.2.3")) end it "should allow ops with multiple spaces" do expect(Chef::VersionConstraint.new("= 1.2.3")).to eql(Chef::VersionConstraint.new("= 1.2.3")) end describe "include?" do describe "handles various input data types" do before do @vc = Chef::VersionConstraint.new "> 1.2.3" end it "String" do expect(@vc).to include "1.4" end it "Chef::Version" do expect(@vc).to include Chef::Version.new("1.4") end it "Chef::CookbookVersion" do cv = Chef::CookbookVersion.new("alice", '/tmp/blah.txt') cv.version = "1.4" expect(@vc).to include cv end end it "strictly less than" do vc = Chef::VersionConstraint.new "< 1.2.3" expect(vc).not_to include "1.3.0" expect(vc).not_to include "1.2.3" expect(vc).to include "1.2.2" end it "strictly greater than" do vc = Chef::VersionConstraint.new "> 1.2.3" expect(vc).to include "1.3.0" expect(vc).not_to include "1.2.3" expect(vc).not_to include "1.2.2" end it "less than or equal to" do vc = Chef::VersionConstraint.new "<= 1.2.3" expect(vc).not_to include "1.3.0" expect(vc).to include "1.2.3" expect(vc).to include "1.2.2" end it "greater than or equal to" do vc = Chef::VersionConstraint.new ">= 1.2.3" expect(vc).to include "1.3.0" expect(vc).to include "1.2.3" expect(vc).not_to include "1.2.2" end it "equal to" do vc = Chef::VersionConstraint.new "= 1.2.3" expect(vc).not_to include "1.3.0" expect(vc).to include "1.2.3" expect(vc).not_to include "0.3.0" end it "pessimistic ~> x.y.z" do vc = Chef::VersionConstraint.new "~> 1.2.3" expect(vc).to include "1.2.3" expect(vc).to include "1.2.4" expect(vc).not_to include "1.2.2" expect(vc).not_to include "1.3.0" expect(vc).not_to include "2.0.0" end it "pessimistic ~> x.y" do vc = Chef::VersionConstraint.new "~> 1.2" expect(vc).to include "1.3.3" expect(vc).to include "1.4" expect(vc).not_to include "2.2" expect(vc).not_to include "0.3.0" end end describe 'to_s' do it 'shows a patch-level if one is given' do vc = Chef::VersionConstraint.new '~> 1.2.0' expect(vc.to_s).to eq('~> 1.2.0') end it 'shows no patch-level if one is not given' do vc = Chef::VersionConstraint.new '~> 1.2' expect(vc.to_s).to eq('~> 1.2') end end describe 'inspect' do it 'shows a patch-level if one is given' do vc = Chef::VersionConstraint.new '~> 1.2.0' expect(vc.inspect).to eq('(~> 1.2.0)') end it 'shows no patch-level if one is not given' do vc = Chef::VersionConstraint.new '~> 1.2' expect(vc.inspect).to eq('(~> 1.2)') end end end chef-12.3.0/spec/unit/policy_builder/0000755000004100000410000000000012520074675017460 5ustar www-datawww-datachef-12.3.0/spec/unit/policy_builder/expand_node_object_spec.rb0000644000004100000410000002451712520074675024642 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/policy_builder' describe Chef::PolicyBuilder::ExpandNodeObject do let(:node_name) { "joe_node" } let(:ohai_data) { {"platform" => "ubuntu", "platform_version" => "13.04", "fqdn" => "joenode.example.com"} } let(:json_attribs) { {"run_list" => []} } let(:override_runlist) { "recipe[foo::default]" } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:policy_builder) { Chef::PolicyBuilder::ExpandNodeObject.new(node_name, ohai_data, json_attribs, override_runlist, events) } # All methods that Chef::Client calls on this class. describe "Public API" do it "implements a node method" do expect(policy_builder).to respond_to(:node) end it "implements a load_node method" do expect(policy_builder).to respond_to(:load_node) end it "implements a build_node method" do expect(policy_builder).to respond_to(:build_node) end it "implements a setup_run_context method that accepts a list of recipe files to run" do expect(policy_builder).to respond_to(:setup_run_context) expect(policy_builder.method(:setup_run_context).arity).to eq(-1) #optional argument end it "implements a run_context method" do expect(policy_builder).to respond_to(:run_context) end it "implements an expand_run_list method" do expect(policy_builder).to respond_to(:expand_run_list) end it "implements a sync_cookbooks method" do expect(policy_builder).to respond_to(:sync_cookbooks) end it "implements a temporary_policy? method" do expect(policy_builder).to respond_to(:temporary_policy?) end describe "loading the node" do context "on chef-solo" do before do Chef::Config[:solo] = true end it "creates a new in-memory node object with the given name" do policy_builder.load_node expect(policy_builder.node.name).to eq(node_name) end end context "on chef-client" do let(:node) { Chef::Node.new.tap { |n| n.name(node_name) } } it "loads or creates a node on the server" do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node expect(policy_builder.node).to eq(node) end end end describe "building the node" do # XXX: Chef::Client just needs to be able to call this, it doesn't depend on the return value. it "builds the node and returns the updated node object" do skip end end end # Implementation specific tests describe "when first created" do it "has a node_name" do expect(policy_builder.node_name).to eq(node_name) end it "has ohai data" do expect(policy_builder.ohai_data).to eq(ohai_data) end it "has a set of attributes from command line option" do expect(policy_builder.json_attribs).to eq(json_attribs) end it "has an override_runlist" do expect(policy_builder.override_runlist).to eq(override_runlist) end end context "once the node has been loaded" do let(:node) do node = Chef::Node.new node.name(node_name) node.run_list(["recipe[a::default]", "recipe[b::server]"]) node end before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node end it "expands the run_list" do expect(policy_builder.expand_run_list).to be_a(Chef::RunList::RunListExpansion) expect(policy_builder.run_list_expansion).to be_a(Chef::RunList::RunListExpansion) expect(policy_builder.run_list_expansion.recipes).to eq(["a::default", "b::server"]) end end describe "building the node" do let(:configured_environment) { nil } let(:json_attribs) { nil } let(:override_runlist) { nil } let(:primary_runlist) { ["recipe[primary::default]"] } let(:original_default_attrs) { {"default_key" => "default_value"} } let(:original_override_attrs) { {"override_key" => "override_value"} } let(:node) do node = Chef::Node.new node.name(node_name) node.default_attrs = original_default_attrs node.override_attrs = original_override_attrs node.run_list(primary_runlist) node end before do Chef::Config[:environment] = configured_environment expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node policy_builder.build_node end it "sanity checks test setup" do expect(node.run_list).to eq(primary_runlist) end it "clears existing default and override attributes from the node" do expect(node["default_key"]).to be_nil expect(node["override_key"]).to be_nil end it "applies ohai data to the node" do expect(node["fqdn"]).to eq(ohai_data["fqdn"]) end it "reports that a temporary_policy is not being used" do expect(policy_builder.temporary_policy?).to be_falsey end describe "when the given run list is not in expanded form" do # NOTE: for chef-client, the behavior is always to expand the run list, # but this operation is a no-op when none of the run list items are # roles. Because of the amount of mocking required to make this work in # tests, this test is isolated from the others. let(:primary_runlist) { ["role[some_role]"] } let(:expansion) do recipe_list = Chef::RunList::VersionedRecipeList.new recipe_list.add_recipe("recipe[from_role::default", "1.0.2") double("RunListExpansion", :recipes => recipe_list) end let(:node) do node = Chef::Node.new node.name(node_name) node.default_attrs = original_default_attrs node.override_attrs = original_override_attrs node.run_list(primary_runlist) expect(node).to receive(:expand!).with("server") do node.run_list("recipe[from_role::default]") expansion end node end it "expands run list items via the server API" do expect(node.run_list).to eq(["recipe[from_role::default]"]) end end context "when JSON attributes are given on the command line" do let(:json_attribs) { {"run_list" => ["recipe[json_attribs::default]"], "json_attribs_key" => "json_attribs_value" } } it "sets the run list according to the given JSON" do expect(node.run_list).to eq(["recipe[json_attribs::default]"]) end it "sets node attributes according to the given JSON" do expect(node["json_attribs_key"]).to eq("json_attribs_value") end end context "when an override_runlist is given" do let(:override_runlist) { "recipe[foo::default]" } it "sets the override run_list on the node" do expect(node.run_list).to eq([override_runlist]) expect(node.primary_runlist).to eq(primary_runlist) end it "reports that a temporary policy is being used" do expect(policy_builder.temporary_policy?).to be_truthy end end context "when no environment is specified" do it "does not set the environment" do expect(node.chef_environment).to eq("_default") end end context "when a custom environment is configured" do let(:configured_environment) { environment.name } let(:environment) do environment = Chef::Environment.new.tap {|e| e.name("prod") } expect(Chef::Environment).to receive(:load).with("prod").and_return(environment) environment end it "sets the environment as configured" do expect(node.chef_environment).to eq(environment.name) end end end describe "configuring the run_context" do let(:json_attribs) { nil } let(:override_runlist) { nil } let(:node) do node = Chef::Node.new node.name(node_name) node.run_list("recipe[first::default]", "recipe[second::default]") node end let(:chef_http) { double("Chef::REST") } let(:cookbook_resolve_url) { "environments/#{node.chef_environment}/cookbook_versions" } let(:cookbook_resolve_post_data) { {:run_list=>["first::default", "second::default"]} } # cookbook_hash is just a hash, but since we're passing it between mock # objects, we get a little better test strictness by using a double (which # will have object equality rather than semantic equality #== semantics). let(:cookbook_hash) { double("cookbook hash", :each => nil) } let(:cookbook_synchronizer) { double("CookbookSynchronizer") } before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) allow(policy_builder).to receive(:api_service).and_return(chef_http) policy_builder.load_node policy_builder.build_node run_list_expansion = policy_builder.run_list_expansion expect(chef_http).to receive(:post).with(cookbook_resolve_url, cookbook_resolve_post_data).and_return(cookbook_hash) expect(Chef::CookbookSynchronizer).to receive(:new).with(cookbook_hash, events).and_return(cookbook_synchronizer) expect(cookbook_synchronizer).to receive(:sync_cookbooks) expect_any_instance_of(Chef::RunContext).to receive(:load).with(run_list_expansion) policy_builder.setup_run_context end it "configures FileVendor to fetch files remotely" do manifest = double("cookbook manifest") expect(Chef::Cookbook::RemoteFileVendor).to receive(:new).with(manifest, chef_http) Chef::Cookbook::FileVendor.create_from_manifest(manifest) end it "triggers cookbook compilation in the run_context" do # Test condition already covered by `Chef::RunContext.any_instance.should_receive(:load).with(run_list_expansion)` end end end chef-12.3.0/spec/unit/policy_builder/policyfile_spec.rb0000644000004100000410000004537212520074675023171 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/policy_builder' describe Chef::PolicyBuilder::Policyfile do let(:node_name) { "joe_node" } let(:ohai_data) { {"platform" => "ubuntu", "platform_version" => "13.04", "fqdn" => "joenode.example.com"} } let(:json_attribs) { {"custom_attr" => "custom_attr_value"} } let(:override_runlist) { nil } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:policy_builder) { Chef::PolicyBuilder::Policyfile.new(node_name, ohai_data, json_attribs, override_runlist, events) } # Convert a SHA1 (160 bit) hex string into an x.y.z version number where the # maximum value is smaller than a postgres BIGINT (signed 64bit, so 63 usable # bits). This requires enterprise Chef or open source server 11.1.0+ (currently not released) # # The SHA1 is devided as follows: # * "major": first 14 chars (56 bits) # * "minor": next 14 chars (56 bits) # * "patch": last 12 chars (48 bits) def id_to_dotted(sha1_id) major = sha1_id[0...14] minor = sha1_id[14...28] patch = sha1_id[28..40] decimal_integers =[major, minor, patch].map {|hex| hex.to_i(16) } decimal_integers.join(".") end let(:example1_lock_data) do # based on https://github.com/danielsdeleo/chef-workflow2-prototype/blob/master/skeletons/basic_policy/Policyfile.lock.json { "identifier" => "168d2102fb11c9617cd8a981166c8adc30a6e915", "version" => "2.3.5", # NOTE: for compatibility mode we include the dotted id in the policyfile to enhance discoverability. "dotted_decimal_identifier" => id_to_dotted("168d2102fb11c9617cd8a981166c8adc30a6e915"), "source" => { "path" => "./cookbooks/demo" }, "scm_identifier"=> { "vcs"=> "git", "rev_id"=> "9d5b09026470c322c3cb5ca8a4157c4d2f16cef3", "remote"=> nil } } end let(:example2_lock_data) do { "identifier" => "feab40e1fca77c7360ccca1481bb8ba5f919ce3a", "version" => "4.2.0", # NOTE: for compatibility mode we include the dotted id in the policyfile to enhance discoverability. "dotted_decimal_identifier" => id_to_dotted("feab40e1fca77c7360ccca1481bb8ba5f919ce3a"), "source" => { "api" => "https://community.getchef.com/api/v1/cookbooks/example2" } } end let(:policyfile_default_attributes) { {"policyfile_default_attr" => "policyfile_default_value"} } let(:policyfile_override_attributes) { {"policyfile_override_attr" => "policyfile_override_value"} } let(:policyfile_run_list) { ["recipe[example1::default]", "recipe[example2::server]"] } let(:parsed_policyfile_json) do { "run_list" => policyfile_run_list, "cookbook_locks" => { "example1" => example1_lock_data, "example2" => example2_lock_data }, "default_attributes" => policyfile_default_attributes, "override_attributes" => policyfile_override_attributes } end let(:err_namespace) { Chef::PolicyBuilder::Policyfile } it "configures a Chef HTTP API client" do http = double("Chef::REST") server_url = "https://api.opscode.com/organizations/example" Chef::Config[:chef_server_url] = server_url expect(Chef::REST).to receive(:new).with(server_url).and_return(http) expect(policy_builder.http_api).to eq(http) end describe "reporting unsupported features" do def initialize_pb Chef::PolicyBuilder::Policyfile.new(node_name, ohai_data, json_attribs, override_runlist, events) end it "always gives `false` for #temporary_policy?" do expect(initialize_pb.temporary_policy?).to be_falsey end context "chef-solo" do before { Chef::Config[:solo] = true } it "errors on create" do expect { initialize_pb }.to raise_error(err_namespace::UnsupportedFeature) end end context "when given an override run_list" do let(:override_runlist) { "recipe[foo],recipe[bar]" } it "errors on create" do expect { initialize_pb }.to raise_error(err_namespace::UnsupportedFeature) end end context "when json_attribs contains a run_list" do let(:json_attribs) { {"run_list" => []} } it "errors on create" do expect { initialize_pb }.to raise_error(err_namespace::UnsupportedFeature) end end context "when an environment is configured" do before { Chef::Config[:environment] = "blurch" } it "errors when an environment is configured" do expect { initialize_pb }.to raise_error(err_namespace::UnsupportedFeature) end end end describe "loading policy data" do let(:http_api) { double("Chef::REST") } let(:configured_environment) { nil } let(:override_runlist) { nil } let(:primary_runlist) { nil } let(:original_default_attrs) { {"default_key" => "default_value"} } let(:original_override_attrs) { {"override_key" => "override_value"} } let(:node) do node = Chef::Node.new node.name(node_name) node.default_attrs = original_default_attrs node.override_attrs = original_override_attrs node.run_list(primary_runlist) if primary_runlist node end before do # TODO: agree on this name and logic. Chef::Config[:deployment_group] = "example-policy-stage" allow(policy_builder).to receive(:http_api).and_return(http_api) end describe "when using compatibility mode (policy_document_native_api == false)" do context "when the deployment group cannot be loaded" do let(:error404) { Net::HTTPServerException.new("404 message", :body) } before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) expect(http_api).to receive(:get). with("data/policyfiles/example-policy-stage"). and_raise(error404) end it "raises an error" do expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError) end it "sends error message to the event system" do expect(events).to receive(:node_load_failed).with(node_name, an_instance_of(err_namespace::ConfigurationError), Chef::Config) expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError) end end context "when the deployment_group is not configured" do before do Chef::Config[:deployment_group] = nil expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) end it "errors while loading the node" do expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError) end it "passes error information to the event system" do # TODO: also make sure something acceptable happens with the error formatters err_class = err_namespace::ConfigurationError expect(events).to receive(:node_load_failed).with(node_name, an_instance_of(err_class), Chef::Config) expect { policy_builder.load_node }.to raise_error(err_class) end end context "when deployment_group is correctly configured" do let(:policy_relative_url) { "data/policyfiles/example-policy-stage" } before do expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json) end it "fetches the policy file from a data bag item" do expect(policy_builder.policy).to eq(parsed_policyfile_json) end it "extracts the run_list from the policyfile" do expect(policy_builder.run_list).to eq(policyfile_run_list) end end end context "and policy_document_native_api is configured" do before do Chef::Config[:policy_document_native_api] = true Chef::Config[:policy_group] = "policy-stage" Chef::Config[:policy_name] = "example" end context "and policy_name or policy_group are not configured" do it "raises a Configuration error for policy_group" do Chef::Config[:policy_group] = nil expect { policy_builder.policy }.to raise_error(err_namespace::ConfigurationError) end it "raises a Configuration error for policy_name" do Chef::Config[:policy_name] = nil expect { policy_builder.policy }.to raise_error(err_namespace::ConfigurationError) end end context "and policy_name and policy_group are configured" do let(:policy_relative_url) { "policy_groups/policy-stage/policies/example" } before do expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json) end it "fetches the policy file from a data bag item" do expect(policy_builder.policy).to eq(parsed_policyfile_json) end it "extracts the run_list from the policyfile" do expect(policy_builder.run_list).to eq(policyfile_run_list) end end end describe "building policy from the policyfile" do before do allow(policy_builder).to receive(:policy).and_return(parsed_policyfile_json) end it "fetches the policy file from a data bag item" do expect(policy_builder.policy).to eq(parsed_policyfile_json) end it "extracts the run_list from the policyfile" do expect(policy_builder.run_list).to eq(policyfile_run_list) end it "extracts the cookbooks and versions for display from the policyfile" do expected = [ "example1::default@2.3.5 (168d210)", "example2::server@4.2.0 (feab40e)" ] expect(policy_builder.run_list_with_versions_for_display).to eq(expected) end it "generates a RunListExpansion-alike object for feeding to the CookbookCompiler" do expect(policy_builder.run_list_expansion_ish).to respond_to(:recipes) expect(policy_builder.run_list_expansion_ish.recipes).to eq(["example1::default", "example2::server"]) end it "implements #expand_run_list in a manner compatible with ExpandNodeObject" do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node expect(policy_builder.expand_run_list).to respond_to(:recipes) expect(policy_builder.expand_run_list.recipes).to eq(["example1::default", "example2::server"]) expect(policy_builder.expand_run_list.roles).to eq([]) end describe "validating the Policyfile.lock" do it "errors if the policyfile json contains any non-recipe items" do parsed_policyfile_json["run_list"] = ["role[foo]"] expect { policy_builder.validate_policyfile }.to raise_error(err_namespace::PolicyfileError) end it "errors if the policyfile json contains non-fully qualified recipe items" do parsed_policyfile_json["run_list"] = ["recipe[foo]"] expect { policy_builder.validate_policyfile }.to raise_error(err_namespace::PolicyfileError) end it "errors if the policyfile doesn't have a run_list key" do parsed_policyfile_json.delete("run_list") expect { policy_builder.validate_policyfile }.to raise_error(err_namespace::PolicyfileError) end it "error if the policyfile doesn't have a cookbook_locks key" do parsed_policyfile_json.delete("cookbook_locks") expect { policy_builder.validate_policyfile }.to raise_error(err_namespace::PolicyfileError) end it "accepts a valid policyfile" do policy_builder.validate_policyfile end end describe "building the node object" do before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node policy_builder.build_node end it "resets default and override data" do expect(node["default_key"]).to be_nil expect(node["override_key"]).to be_nil end it "applies ohai data" do expect(ohai_data).to_not be_empty # ensure test is testing something ohai_data.each do |key, value| expect(node.automatic_attrs[key]).to eq(value) end end it "applies attributes from json file" do expect(node["custom_attr"]).to eq("custom_attr_value") end it "applies attributes from the policyfile" do expect(node["policyfile_default_attr"]).to eq("policyfile_default_value") expect(node["policyfile_override_attr"]).to eq("policyfile_override_value") end it "sets the policyfile's run_list on the node object" do expect(node.run_list).to eq(policyfile_run_list) end it "creates node.automatic_attrs[:roles]" do expect(node.automatic_attrs[:roles]).to eq([]) end it "create node.automatic_attrs[:recipes]" do expect(node.automatic_attrs[:recipes]).to eq(["example1::default", "example2::server"]) end end describe "fetching the desired cookbook set" do let(:example1_cookbook_data) { double("CookbookVersion Hash for example1 cookbook") } let(:example2_cookbook_data) { double("CookbookVersion Hash for example2 cookbook") } let(:example1_cookbook_object) { double("Chef::CookbookVersion for example1 cookbook") } let(:example2_cookbook_object) { double("Chef::CookbookVersion for example2 cookbook") } let(:expected_cookbook_hash) do { "example1" => example1_cookbook_object, "example2" => example2_cookbook_object } end let(:example1_xyz_version) { example1_lock_data["dotted_decimal_identifier"] } let(:example2_xyz_version) { example2_lock_data["dotted_decimal_identifier"] } let(:example1_identifier) { example1_lock_data["identifier"] } let(:example2_identifier) { example2_lock_data["identifier"] } let(:cookbook_synchronizer) { double("Chef::CookbookSynchronizer") } shared_examples "fetching cookbooks when they don't exist" do context "and a cookbook is missing" do let(:error404) { Net::HTTPServerException.new("404 message", :body) } before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node policy_builder.build_node expect(http_api).to receive(:get).with(cookbook1_url). and_raise(error404) end it "raises an error indicating which cookbook is missing" do expect { policy_builder.cookbooks_to_sync }.to raise_error(Chef::Exceptions::CookbookNotFound) end end end shared_examples_for "fetching cookbooks when they exist" do context "and the cookbooks can be fetched" do before do expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node) policy_builder.load_node policy_builder.build_node allow(Chef::CookbookSynchronizer).to receive(:new). with(expected_cookbook_hash, events). and_return(cookbook_synchronizer) end it "builds a Hash of the form 'cookbook_name' => Chef::CookbookVersion" do expect(policy_builder.cookbooks_to_sync).to eq(expected_cookbook_hash) end it "syncs the desired cookbooks via CookbookSynchronizer" do expect(cookbook_synchronizer).to receive(:sync_cookbooks) policy_builder.sync_cookbooks end it "builds a run context" do expect(cookbook_synchronizer).to receive(:sync_cookbooks) expect_any_instance_of(Chef::RunContext).to receive(:load).with(policy_builder.run_list_expansion_ish) run_context = policy_builder.setup_run_context expect(run_context.node).to eq(node) expect(run_context.cookbook_collection.keys).to match_array(["example1", "example2"]) end end end # shared_examples_for "fetching cookbooks" context "when using compatibility mode (policy_document_native_api == false)" do let(:cookbook1_url) { "cookbooks/example1/#{example1_xyz_version}" } let(:cookbook2_url) { "cookbooks/example2/#{example2_xyz_version}" } context "when the cookbooks don't exist on the server" do include_examples "fetching cookbooks when they don't exist" end context "when the cookbooks exist on the server" do before do expect(http_api).to receive(:get).with(cookbook1_url). and_return(example1_cookbook_object) expect(http_api).to receive(:get).with(cookbook2_url). and_return(example2_cookbook_object) end include_examples "fetching cookbooks when they exist" end end context "when using native API mode (policy_document_native_api == true)" do before do Chef::Config[:policy_document_native_api] = true Chef::Config[:policy_group] = "policy-stage" Chef::Config[:policy_name] = "example" end let(:cookbook1_url) { "cookbook_artifacts/example1/#{example1_identifier}" } let(:cookbook2_url) { "cookbook_artifacts/example2/#{example2_identifier}" } context "when the cookbooks don't exist on the server" do include_examples "fetching cookbooks when they don't exist" end context "when the cookbooks exist on the server" do before do expect(http_api).to receive(:get).with(cookbook1_url). and_return(example1_cookbook_data) expect(http_api).to receive(:get).with(cookbook2_url). and_return(example2_cookbook_data) expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data). and_return(example1_cookbook_object) expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data). and_return(example2_cookbook_object) end include_examples "fetching cookbooks when they exist" end end end end end end chef-12.3.0/spec/unit/cookbook_manifest_spec.rb0000644000004100000410000001753712520074675021523 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2015 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/cookbook_manifest' require 'chef/digester' require 'pathname' describe Chef::CookbookManifest do let(:version) { "1.2.3" } let(:identifier) { "9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b" } let(:metadata) do Chef::Cookbook::Metadata.new.tap do |m| m.version(version) end end let(:cookbook_root) { '/tmp/blah' } let(:cookbook_version) do Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c| c.metadata = metadata c.identifier = identifier end end let(:policy_mode) { false } subject(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version, policy_mode: policy_mode) } context "when policy mode is not specified" do subject(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version) } it "defaults to policies disabled" do expect(cookbook_manifest.policy_mode?).to be(false) end end describe "collecting cookbook data from the cookbook version object" do it "delegates `name' to cookbook_version" do expect(cookbook_manifest.name).to eq("tatft") end it "delegates `root_paths' to cookbook_version" do expect(cookbook_manifest.root_paths).to eq(['/tmp/blah']) end it "delegates `metadata' to cookbook_version" do expect(cookbook_manifest.metadata).to eq(metadata) end it "delegates `full_name' to cookbook_version" do expect(cookbook_manifest.full_name).to eq("tatft-1.2.3") end it "delegates `version' to cookbook_version" do expect(cookbook_manifest.version).to eq(version) end it "delegates `frozen_version?' to cookbook_version" do expect(cookbook_manifest.frozen_version?).to be(false) end it "delegates `segment_filenames' to cookbook_version" do expect(cookbook_version).to receive(:segment_filenames).with(:recipes).and_return([]) expect(cookbook_manifest.segment_filenames(:recipes)).to eq([]) end end context "when given an empty cookbook" do let(:expected_hash) do { "chef_type" => "cookbook_version", "name" => "tatft-1.2.3", "version" => "1.2.3", "cookbook_name" => "tatft", "metadata" => metadata, "frozen?" => false, "recipes" =>[], "definitions" =>[], "libraries" =>[], "attributes" =>[], "files" =>[], "templates" =>[], "resources" =>[], "providers" =>[], "root_files" =>[], } end it "converts the CookbookVersion to a ruby Hash representation" do expect(cookbook_manifest.to_hash).to eq(expected_hash) end end context "when given a cookbook with files" do let(:cookbook_root) { File.join(CHEF_SPEC_DATA, 'cb_version_cookbooks', 'tatft') } let(:attribute_filenames) { Dir[File.join(cookbook_root, 'attributes', '**', '*.rb')] } let(:definition_filenames) { Dir[File.join(cookbook_root, 'definitions', '**', '*.rb')] } let(:file_filenames) { Dir[File.join(cookbook_root, 'files', '**', '*.tgz')] } let(:recipe_filenames) { Dir[File.join(cookbook_root, 'recipes', '**', '*.rb')] } let(:template_filenames) { Dir[File.join(cookbook_root, 'templates', '**', '*.erb')] } let(:library_filenames) { Dir[File.join(cookbook_root, 'libraries', '**', '*.rb')] } let(:resource_filenames) { Dir[File.join(cookbook_root, 'resources', '**', '*.rb')] } let(:provider_filenames) { Dir[File.join(cookbook_root, 'providers', '**', '*.rb')] } let(:root_filenames) { Array(File.join(cookbook_root, 'README.rdoc')) } let(:metadata_filenames) { Array(File.join(cookbook_root, 'metadata.json')) } let(:match_md5) { /[0-9a-f]{32}/ } def map_to_file_specs(paths) paths.map do |path| relative_path = Pathname.new(path).relative_path_from(Pathname.new(cookbook_root)).to_s { "name" => File.basename(path), "path" => relative_path, "checksum" => Chef::Digester.generate_md5_checksum_for_file(path), "specificity" => "default", } end end let(:expected_hash) do { "chef_type" => "cookbook_version", "name" => "tatft-1.2.3", "version" => "1.2.3", "cookbook_name" => "tatft", "metadata" => metadata, "frozen?" => false, "recipes" => map_to_file_specs(recipe_filenames), "definitions" => map_to_file_specs(definition_filenames), "libraries" => map_to_file_specs(library_filenames), "attributes" => map_to_file_specs(attribute_filenames), "files" => map_to_file_specs(file_filenames), "templates" => map_to_file_specs(template_filenames), "resources" => map_to_file_specs(resource_filenames), "providers" => map_to_file_specs(provider_filenames), "root_files" => map_to_file_specs(root_filenames), } end before do cookbook_version.attribute_filenames = attribute_filenames cookbook_version.definition_filenames = definition_filenames cookbook_version.file_filenames = file_filenames cookbook_version.recipe_filenames = recipe_filenames cookbook_version.template_filenames = template_filenames cookbook_version.library_filenames = library_filenames cookbook_version.resource_filenames = resource_filenames cookbook_version.provider_filenames = provider_filenames cookbook_version.root_filenames = root_filenames cookbook_version.metadata_filenames = metadata_filenames end it "converts the CookbookVersion to a ruby Hash representation" do cookbook_manifest_hash = cookbook_manifest.to_hash expect(cookbook_manifest_hash.keys).to match_array(expected_hash.keys) cookbook_manifest_hash.each do |key, value| expect(cookbook_manifest_hash[key]).to eq(expected_hash[key]) end end end describe "providing upstream URLs for save" do context "and policy mode is disabled" do it "gives the save URL" do expect(cookbook_manifest.save_url).to eq("cookbooks/tatft/1.2.3") end it "gives the force save URL" do expect(cookbook_manifest.force_save_url).to eq("cookbooks/tatft/1.2.3?force=true") end end context "and policy mode is enabled" do let(:policy_mode) { true } let(:cookbook_manifest_hash) { cookbook_manifest.to_hash } it "sets the identifier in the manifest data" do expect(cookbook_manifest_hash["identifier"]).to eq("9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b") end it "sets the name to just the name" do expect(cookbook_manifest_hash["name"]).to eq("tatft") end it "does not set a 'cookbook_name' field" do expect(cookbook_manifest_hash).to_not have_key("cookbook_name") end it "gives the save URL" do expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b") end it "gives the force save URL" do expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b?force=true") end end end end chef-12.3.0/spec/unit/api_client_spec.rb0000644000004100000410000002440312520074675020124 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/api_client' require 'tempfile' describe Chef::ApiClient do before(:each) do @client = Chef::ApiClient.new end it "has a name attribute" do @client.name("ops_master") expect(@client.name).to eq("ops_master") end it "does not allow spaces in the name" do expect { @client.name "ops master" }.to raise_error(ArgumentError) end it "only allows string values for the name" do expect { @client.name Hash.new }.to raise_error(ArgumentError) end it "has an admin flag attribute" do @client.admin(true) expect(@client.admin).to be_truthy end it "defaults to non-admin" do expect(@client.admin).to be_falsey end it "allows only boolean values for the admin flag" do expect { @client.admin(false) }.not_to raise_error expect { @client.admin(Hash.new) }.to raise_error(ArgumentError) end it "has a 'validator' flag attribute" do @client.validator(true) expect(@client.validator).to be_truthy end it "defaults to non-validator" do expect(@client.validator).to be_falsey end it "allows only boolean values for the 'validator' flag" do expect { @client.validator(false) }.not_to raise_error expect { @client.validator(Hash.new) }.to raise_error(ArgumentError) end it "has a public key attribute" do @client.public_key("super public") expect(@client.public_key).to eq("super public") end it "accepts only String values for the public key" do expect { @client.public_key "" }.not_to raise_error expect { @client.public_key Hash.new }.to raise_error(ArgumentError) end it "has a private key attribute" do @client.private_key("super private") expect(@client.private_key).to eq("super private") end it "accepts only String values for the private key" do expect { @client.private_key "" }.not_to raise_error expect { @client.private_key Hash.new }.to raise_error(ArgumentError) end describe "when serializing to JSON" do before(:each) do @client.name("black") @client.public_key("crowes") @json = @client.to_json end it "serializes as a JSON object" do expect(@json).to match(/^\{.+\}$/) end it "includes the name value" do expect(@json).to include(%q{"name":"black"}) end it "includes the public key value" do expect(@json).to include(%{"public_key":"crowes"}) end it "includes the 'admin' flag" do expect(@json).to include(%q{"admin":false}) end it "includes the 'validator' flag" do expect(@json).to include(%q{"validator":false}) end it "includes the private key when present" do @client.private_key("monkeypants") expect(@client.to_json).to include(%q{"private_key":"monkeypants"}) end it "does not include the private key if not present" do expect(@json).not_to include("private_key") end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @client } end end describe "when deserializing from JSON (string) using ApiClient#from_json" do let(:client_string) do "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true}" end let(:client) do Chef::ApiClient.from_json(client_string) end it "does not require a 'json_class' string" do expect(Chef::JSONCompat.parse(client_string)["json_class"]).to eq(nil) end it "should deserialize to a Chef::ApiClient object" do expect(client).to be_a_kind_of(Chef::ApiClient) end it "preserves the name" do expect(client.name).to eq("black") end it "preserves the public key" do expect(client.public_key).to eq("crowes") end it "preserves the admin status" do expect(client.admin).to be_truthy end it "preserves the 'validator' status" do expect(client.validator).to be_truthy end it "includes the private key if present" do expect(client.private_key).to eq("monkeypants") end end describe "when deserializing from JSON (hash) using JSONCompat#from_json" do let(:client_hash) do { "name" => "black", "public_key" => "crowes", "private_key" => "monkeypants", "admin" => true, "validator" => true, "json_class" => "Chef::ApiClient" } end let(:client) do Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(client_hash)) end it "should deserialize to a Chef::ApiClient object" do expect(client).to be_a_kind_of(Chef::ApiClient) end it "preserves the name" do expect(client.name).to eq("black") end it "preserves the public key" do expect(client.public_key).to eq("crowes") end it "preserves the admin status" do expect(client.admin).to be_truthy end it "preserves the 'validator' status" do expect(client.validator).to be_truthy end it "includes the private key if present" do expect(client.private_key).to eq("monkeypants") end end describe "when loading from JSON" do before do end before(:each) do client = { "name" => "black", "clientname" => "black", "public_key" => "crowes", "private_key" => "monkeypants", "admin" => true, "validator" => true, "json_class" => "Chef::ApiClient" } @http_client = double("Chef::REST mock") allow(Chef::REST).to receive(:new).and_return(@http_client) expect(@http_client).to receive(:get).with("clients/black").and_return(client) @client = Chef::ApiClient.load(client['name']) end it "should deserialize to a Chef::ApiClient object" do expect(@client).to be_a_kind_of(Chef::ApiClient) end it "preserves the name" do expect(@client.name).to eq("black") end it "preserves the public key" do expect(@client.public_key).to eq("crowes") end it "preserves the admin status" do expect(@client.admin).to be_a_kind_of(TrueClass) end it "preserves the 'validator' status" do expect(@client.validator).to be_a_kind_of(TrueClass) end it "includes the private key if present" do expect(@client.private_key).to eq("monkeypants") end end describe "with correctly configured API credentials" do before do Chef::Config[:node_name] = "silent-bob" Chef::Config[:client_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA) end after do Chef::Config[:node_name] = nil Chef::Config[:client_key] = nil end let :private_key_data do File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp } end it "has an HTTP client configured with default credentials" do expect(@client.http_api).to be_a_kind_of(Chef::REST) expect(@client.http_api.client_name).to eq("silent-bob") expect(@client.http_api.signing_key.to_s).to eq(private_key_data) end end describe "when requesting a new key" do before do @http_client = double("Chef::REST mock") allow(Chef::REST).to receive(:new).and_return(@http_client) end context "and the client does not exist on the server" do before do @a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil) @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response) expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception) end it "raises a 404 error" do expect { Chef::ApiClient.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException) end end context "and the client exists" do before do @api_client_without_key = Chef::ApiClient.new @api_client_without_key.name("lost-my-key") expect(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key) end context "and the client exists on a Chef 11-like server" do before do @api_client_with_key = Chef::ApiClient.new @api_client_with_key.name("lost-my-key") @api_client_with_key.private_key("the new private key") expect(@http_client).to receive(:put). with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true). and_return(@api_client_with_key) end it "returns an ApiClient with a private key" do response = Chef::ApiClient.reregister("lost-my-key") # no sane == method for ApiClient :'( expect(response).to eq(@api_client_without_key) expect(response.private_key).to eq("the new private key") expect(response.name).to eq("lost-my-key") expect(response.admin).to be_falsey end end context "and the client exists on a Chef 10-like server" do before do @api_client_with_key = {"name" => "lost-my-key", "private_key" => "the new private key"} expect(@http_client).to receive(:put). with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true). and_return(@api_client_with_key) end it "returns an ApiClient with a private key" do response = Chef::ApiClient.reregister("lost-my-key") # no sane == method for ApiClient :'( expect(response).to eq(@api_client_without_key) expect(response.private_key).to eq("the new private key") expect(response.name).to eq("lost-my-key") expect(response.admin).to be_falsey expect(response.validator).to be_falsey end end end end end chef-12.3.0/spec/unit/runner_spec.rb0000644000004100000410000003554312520074675017335 0ustar www-datawww-data # Author:: Adam Jacob () # 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' class SnitchyProvider < Chef::Provider def self.all_actions_called @all_actions_called ||= [] end def self.action_called(action) all_actions_called << action end def self.clear_action_record @all_actions_called = nil end def load_current_resource true end def action_first_action @new_resource.updated_by_last_action(true) self.class.action_called(:first) end def action_second_action @new_resource.updated_by_last_action(true) self.class.action_called(:second) end def action_third_action @new_resource.updated_by_last_action(true) self.class.action_called(:third) end end class FailureResource < Chef::Resource attr_accessor :action def initialize(*args) super @action = :fail end def provider FailureProvider end end class FailureProvider < Chef::Provider class ChefClientFail < StandardError; end def load_current_resource true end def action_fail raise ChefClientFail, "chef had an error of some sort" end end describe Chef::Runner do let(:node) do node = Chef::Node.new node.name "latte" node.automatic[:platform] = "mac_os_x" node.automatic[:platform_version] = "10.5.1" node end let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, Chef::CookbookCollection.new({}), events) } let(:first_resource) { Chef::Resource::Cat.new("loulou1", run_context) } let(:runner) { Chef::Runner.new(run_context) } before do run_context.resource_collection << first_resource end context "when we fall through to old Chef::Platform resolution" do let(:provider_resolver) { Chef::ProviderResolver.new(node, first_resource, nil) } before do # set up old Chef::Platform resolution instead of provider_resolver Chef::Platform.set( :resource => :cat, :provider => Chef::Provider::SnakeOil ) allow(Chef::ProviderResolver).to receive(:new).and_return(provider_resolver) allow(provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil) end it "should use the platform provider if it has one" do expect(Chef::Platform).to receive(:find_provider_for_node).with(node, first_resource).and_call_original runner.converge end end context "when we are doing dynamic provider resolution" do it "should pass each resource in the collection to a provider" do expect(run_context.resource_collection).to receive(:execute_each_resource).once runner.converge end it "should use the provider specified by the resource (if it has one)" do provider = Chef::Provider::Easy.new(run_context.resource_collection[0], run_context) # Expect provider to be called twice, because will fall back to old provider lookup expect(run_context.resource_collection[0]).to receive(:provider).twice.and_return(Chef::Provider::Easy) expect(Chef::Provider::Easy).to receive(:new).once.and_return(provider) runner.converge end it "should run the action for each resource" do provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) expect(provider).to receive(:action_sell).once.and_return(true) expect(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) runner.converge end it "should raise exceptions as thrown by a provider" do provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) expect { runner.converge }.to raise_error(ArgumentError) end it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do allow(run_context.resource_collection[0]).to receive(:ignore_failure).and_return(true) provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) expect { runner.converge }.not_to raise_error end it "should retry with the specified delay if retries are specified" do first_resource.retries 3 provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) allow(provider).to receive(:action_sell).and_raise(ArgumentError) expect(first_resource).to receive(:sleep).with(2).exactly(3).times expect { runner.converge }.to raise_error(ArgumentError) end it "should execute immediate actions on changed resources" do notifying_resource = Chef::Resource::Cat.new("peanut", run_context) notifying_resource.action = :purr # only action that will set updated on the resource run_context.resource_collection << notifying_resource first_resource.action = :nothing # won't be updated unless notified by other resource notifying_resource.notifies(:purr, first_resource, :immediately) runner.converge expect(first_resource).to be_updated end it "should follow a chain of actions" do first_resource.action = :nothing middle_resource = Chef::Resource::Cat.new("peanut", run_context) middle_resource.action = :nothing run_context.resource_collection << middle_resource middle_resource.notifies(:purr, first_resource, :immediately) last_resource = Chef::Resource::Cat.new("snuffles", run_context) last_resource.action = :purr run_context.resource_collection << last_resource last_resource.notifies(:purr, middle_resource, :immediately) runner.converge expect(last_resource).to be_updated # by action(:purr) expect(middle_resource).to be_updated # by notification from last_resource expect(first_resource).to be_updated # by notification from middle_resource end it "should execute delayed actions on changed resources" do first_resource.action = :nothing second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :purr run_context.resource_collection << second_resource second_resource.notifies(:purr, first_resource, :delayed) runner.converge expect(first_resource).to be_updated end it "should execute delayed notifications when a failure occurs in the chef client run" do first_resource.action = :nothing second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :purr run_context.resource_collection << second_resource second_resource.notifies(:purr, first_resource, :delayed) third_resource = FailureResource.new("explode", run_context) run_context.resource_collection << third_resource expect { runner.converge }.to raise_error(FailureProvider::ChefClientFail) expect(first_resource).to be_updated end it "should execute delayed notifications when a failure occurs in a notification" do first_resource.action = :nothing second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :purr run_context.resource_collection << second_resource third_resource = FailureResource.new("explode", run_context) third_resource.action = :nothing run_context.resource_collection << third_resource second_resource.notifies(:fail, third_resource, :delayed) second_resource.notifies(:purr, first_resource, :delayed) expect {runner.converge}.to raise_error(FailureProvider::ChefClientFail) expect(first_resource).to be_updated end it "should execute delayed notifications when a failure occurs in multiple notifications" do first_resource.action = :nothing second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :purr run_context.resource_collection << second_resource third_resource = FailureResource.new("explode", run_context) third_resource.action = :nothing run_context.resource_collection << third_resource fourth_resource = FailureResource.new("explode again", run_context) fourth_resource.action = :nothing run_context.resource_collection << fourth_resource second_resource.notifies(:fail, third_resource, :delayed) second_resource.notifies(:fail, fourth_resource, :delayed) second_resource.notifies(:purr, first_resource, :delayed) exception = nil begin runner.converge rescue => e exception = e end expect(exception).to be_a(Chef::Exceptions::MultipleFailures) expected_message =<<-E Multiple failures occurred: * FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort * FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort E expect(exception.message).to eq(expected_message) expect(first_resource).to be_updated end it "does not duplicate delayed notifications" do SnitchyProvider.clear_action_record first_resource.action = :nothing first_resource.provider = SnitchyProvider second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :first_action second_resource.provider = SnitchyProvider run_context.resource_collection << second_resource third_resource = Chef::Resource::Cat.new("snickers", run_context) third_resource.action = :first_action third_resource.provider = SnitchyProvider run_context.resource_collection << third_resource second_resource.notifies(:second_action, first_resource, :delayed) second_resource.notifies(:third_action, first_resource, :delayed) third_resource.notifies(:second_action, first_resource, :delayed) third_resource.notifies(:third_action, first_resource, :delayed) runner.converge # resources 2 and 3 call :first_action in the course of normal resource # execution, and schedule delayed actions :second and :third on the first # resource. The duplicate actions should "collapse" to a single notification # and order should be preserved. expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) end it "executes delayed notifications in the order they were declared" do SnitchyProvider.clear_action_record first_resource.action = :nothing first_resource.provider = SnitchyProvider second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :first_action second_resource.provider = SnitchyProvider run_context.resource_collection << second_resource third_resource = Chef::Resource::Cat.new("snickers", run_context) third_resource.action = :first_action third_resource.provider = SnitchyProvider run_context.resource_collection << third_resource second_resource.notifies(:second_action, first_resource, :delayed) second_resource.notifies(:second_action, first_resource, :delayed) third_resource.notifies(:third_action, first_resource, :delayed) third_resource.notifies(:third_action, first_resource, :delayed) runner.converge expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) end it "does not fire notifications if the resource was not updated by the last action executed" do # REGRESSION TEST FOR CHEF-1452 SnitchyProvider.clear_action_record first_resource.action = :first_action first_resource.provider = SnitchyProvider second_resource = Chef::Resource::Cat.new("peanut", run_context) second_resource.action = :nothing second_resource.provider = SnitchyProvider run_context.resource_collection << second_resource third_resource = Chef::Resource::Cat.new("snickers", run_context) third_resource.action = :nothing third_resource.provider = SnitchyProvider run_context.resource_collection << third_resource first_resource.notifies(:second_action, second_resource, :immediately) second_resource.notifies(:third_action, third_resource, :immediately) runner.converge # All of the resources should only fire once: expect(SnitchyProvider.all_actions_called).to eq([:first, :second, :third]) # all of the resources should be marked as updated for reporting purposes expect(first_resource).to be_updated expect(second_resource).to be_updated expect(third_resource).to be_updated end it "should check a resource's only_if and not_if if notified by another resource" do first_resource.action = :buy only_if_called_times = 0 first_resource.only_if {only_if_called_times += 1; true} not_if_called_times = 0 first_resource.not_if {not_if_called_times += 1; false} second_resource = Chef::Resource::Cat.new("carmel", run_context) run_context.resource_collection << second_resource second_resource.notifies(:purr, first_resource, :delayed) second_resource.action = :purr # hits only_if first time when the resource is run in order, second on notify runner.converge expect(only_if_called_times).to eq(2) expect(not_if_called_times).to eq(2) end it "should resolve resource references in notifications when resources are defined lazily" do first_resource.action = :nothing lazy_resources = lambda { last_resource = Chef::Resource::Cat.new("peanut", run_context) run_context.resource_collection << last_resource last_resource.notifies(:purr, first_resource.to_s, :delayed) last_resource.action = :purr } second_resource = Chef::Resource::RubyBlock.new("myblock", run_context) run_context.resource_collection << second_resource second_resource.block { lazy_resources.call } runner.converge expect(first_resource).to be_updated end end end chef-12.3.0/spec/unit/provider/0000755000004100000410000000000012520074675016305 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/route_spec.rb0000644000004100000410000002452612520074675021013 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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::Route do before do @node = Chef::Node.new @cookbook_collection = Chef::CookbookCollection.new([]) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @new_resource = Chef::Resource::Route.new('10.0.0.10') @new_resource.gateway "10.0.0.9" @current_resource = Chef::Resource::Route.new('10.0.0.10') @current_resource.gateway "10.0.0.9" @provider = Chef::Provider::Route.new(@new_resource, @run_context) @provider.current_resource = @current_resource end describe Chef::Provider::Route, "hex2ip" do it "should return nil if ip address is invalid" do expect(@provider.hex2ip('foo')).to be_nil # does not even look like an ip expect(@provider.hex2ip('ABCDEFGH')).to be_nil # 8 chars, but invalid end it "should return quad-dotted notation for a valid IP" do expect(@provider.hex2ip('01234567')).to eq('103.69.35.1') expect(@provider.hex2ip('0064a8c0')).to eq('192.168.100.0') expect(@provider.hex2ip('00FFFFFF')).to eq('255.255.255.0') end end describe Chef::Provider::Route, "load_current_resource" do context "on linux" do before do @node.automatic_attrs[:os] = 'linux' routing_table = "Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT\n" + "eth0 0064A8C0 0984A8C0 0003 0 0 0 00FFFFFF 0 0 0\n" route_file = StringIO.new(routing_table) allow(File).to receive(:open).with("/proc/net/route", "r").and_return(route_file) end it "should set is_running to false when a route is not detected" do resource = Chef::Resource::Route.new('10.10.10.0/24') allow(resource).to receive(:gateway).and_return("10.0.0.1") allow(resource).to receive(:device).and_return("eth0") provider = Chef::Provider::Route.new(resource, @run_context) provider.load_current_resource expect(provider.is_running).to be_falsey end it "should detect existing routes and set is_running attribute correctly" do resource = Chef::Resource::Route.new('192.168.100.0/24') allow(resource).to receive(:gateway).and_return("192.168.132.9") allow(resource).to receive(:device).and_return("eth0") provider = Chef::Provider::Route.new(resource, @run_context) provider.load_current_resource expect(provider.is_running).to be_truthy end it "should use gateway value when matching routes" do resource = Chef::Resource::Route.new('192.168.100.0/24') allow(resource).to receive(:gateway).and_return("10.10.10.10") allow(resource).to receive(:device).and_return("eth0") provider = Chef::Provider::Route.new(resource, @run_context) provider.load_current_resource expect(provider.is_running).to be_falsey end end end describe Chef::Provider::Route, "action_add" do it "should add the route if it does not exist" do allow(@provider).to receive(:run_command).and_return(true) allow(@current_resource).to receive(:gateway).and_return(nil) expect(@provider).to receive(:generate_command).once.with(:add) expect(@provider).to receive(:generate_config) @provider.run_action(:add) expect(@new_resource).to be_updated end it "should not add the route if it exists" do allow(@provider).to receive(:run_command).and_return(true) allow(@provider).to receive(:is_running).and_return(true) expect(@provider).not_to receive(:generate_command).with(:add) expect(@provider).to receive(:generate_config) @provider.run_action(:add) expect(@new_resource).not_to be_updated end it "should not delete config file for :add action (CHEF-3332)" do @node.automatic_attrs[:platform] = 'centos' route_file = StringIO.new expect(File).to receive(:new).and_return(route_file) @resource_add = Chef::Resource::Route.new('192.168.1.0/24 via 192.168.0.1') @run_context.resource_collection << @resource_add allow(@provider).to receive(:run_command).and_return(true) @resource_add.action(:add) @provider.run_action(:add) expect(route_file.string.split("\n").size).to eq(1) expect(route_file.string).to match(/^192\.168\.1\.0\/24 via 192\.168\.0\.1$/) end end describe Chef::Provider::Route, "action_delete" do it "should delete the route if it exists" do allow(@provider).to receive(:run_command).and_return(true) expect(@provider).to receive(:generate_command).once.with(:delete) allow(@provider).to receive(:is_running).and_return(true) @provider.run_action(:delete) expect(@new_resource).to be_updated end it "should not delete the route if it does not exist" do allow(@current_resource).to receive(:gateway).and_return(nil) allow(@provider).to receive(:run_command).and_return(true) expect(@provider).not_to receive(:generate_command).with(:add) @provider.run_action(:delete) expect(@new_resource).not_to be_updated end end describe Chef::Provider::Route, "generate_command for action_add" do it "should include a netmask when a one is specified" do allow(@new_resource).to receive(:netmask).and_return('255.255.0.0') expect(@provider.generate_command(:add)).to match(/\/\d{1,2}\s/) end it "should not include a netmask when a one is specified" do allow(@new_resource).to receive(:netmask).and_return(nil) expect(@provider.generate_command(:add)).not_to match(/\/\d{1,2}\s/) end it "should include ' via $gateway ' when a gateway is specified" do expect(@provider.generate_command(:add)).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/) end it "should not include ' via $gateway ' when a gateway is not specified" do allow(@new_resource).to receive(:gateway).and_return(nil) expect(@provider.generate_command(:add)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/) end end describe Chef::Provider::Route, "generate_command for action_delete" do it "should include a netmask when a one is specified" do allow(@new_resource).to receive(:netmask).and_return('255.255.0.0') expect(@provider.generate_command(:delete)).to match(/\/\d{1,2}\s/) end it "should not include a netmask when a one is specified" do allow(@new_resource).to receive(:netmask).and_return(nil) expect(@provider.generate_command(:delete)).not_to match(/\/\d{1,2}\s/) end it "should include ' via $gateway ' when a gateway is specified" do expect(@provider.generate_command(:delete)).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/) end it "should not include ' via $gateway ' when a gateway is not specified" do allow(@new_resource).to receive(:gateway).and_return(nil) expect(@provider.generate_command(:delete)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\s/) end end describe Chef::Provider::Route, "config_file_contents for action_add" do it "should include a netmask when a one is specified" do allow(@new_resource).to receive(:netmask).and_return('255.255.0.0') expect(@provider.config_file_contents(:add, { :target => @new_resource.target, :netmask => @new_resource.netmask})).to match(/\/\d{1,2}.*\n$/) end it "should not include a netmask when a one is specified" do expect(@provider.config_file_contents(:add, { :target => @new_resource.target})).not_to match(/\/\d{1,2}.*\n$/) end it "should include ' via $gateway ' when a gateway is specified" do expect(@provider.config_file_contents(:add, { :target => @new_resource.target, :gateway => @new_resource.gateway})).to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/) end it "should not include ' via $gateway ' when a gateway is not specified" do expect(@provider.generate_command(:add)).not_to match(/\svia\s#{Regexp.escape(@new_resource.gateway.to_s)}\n/) end end describe Chef::Provider::Route, "config_file_contents for action_delete" do it "should return an empty string" do expect(@provider.config_file_contents(:delete)).to match(/^$/) end end describe Chef::Provider::Route, "generate_config method" do %w[ centos redhat fedora ].each do |platform| it "should write a route file on #{platform} platform" do @node.automatic_attrs[:platform] = platform route_file = StringIO.new expect(File).to receive(:new).with("/etc/sysconfig/network-scripts/route-eth0", "w").and_return(route_file) #Chef::Log.should_receive(:debug).with("route[10.0.0.10] writing route.eth0\n10.0.0.10 via 10.0.0.9\n") @run_context.resource_collection << @new_resource @provider.generate_config end end it "should put all routes for a device in a route config file" do @node.automatic_attrs[:platform] = 'centos' route_file = StringIO.new expect(File).to receive(:new).and_return(route_file) @run_context.resource_collection << Chef::Resource::Route.new('192.168.1.0/24 via 192.168.0.1') @run_context.resource_collection << Chef::Resource::Route.new('192.168.2.0/24 via 192.168.0.1') @run_context.resource_collection << Chef::Resource::Route.new('192.168.3.0/24 via 192.168.0.1') @provider.action = :add @provider.generate_config expect(route_file.string.split("\n").size).to eq(3) expect(route_file.string).to match(/^192\.168\.1\.0\/24 via 192\.168\.0\.1$/) expect(route_file.string).to match(/^192\.168\.2\.0\/24 via 192\.168\.0\.1$/) expect(route_file.string).to match(/^192\.168\.3\.0\/24 via 192\.168\.0\.1$/) end end end chef-12.3.0/spec/unit/provider/git_spec.rb0000644000004100000410000010534112520074675020433 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Git do before(:each) do allow(STDOUT).to receive(:tty?).and_return(true) @original_log_level = Chef::Log.level Chef::Log.level = :info @current_resource = Chef::Resource::Git.new("web2.0 app") @current_resource.revision("d35af14d41ae22b19da05d7d03a0bafc321b244c") @resource = Chef::Resource::Git.new("web2.0 app") @resource.repository "git://github.com/opscode/chef.git" @resource.destination "/my/deploy/dir" @resource.revision "d35af14d41ae22b19da05d7d03a0bafc321b244c" @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @provider = Chef::Provider::Git.new(@resource, @run_context) @provider.current_resource = @current_resource end after(:each) do Chef::Log.level = @original_log_level end context "determining the revision of the currently deployed checkout" do before do @stdout = double("standard out") @stderr = double("standard error") @exitstatus = double("exitstatus") end it "sets the current revision to nil if the deploy dir does not exist" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(false) expect(@provider.find_current_revision).to be_nil end it "determines the current revision when there is one" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true) @stdout = "9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13\n" expect(@provider).to receive(:shell_out!).with('git rev-parse HEAD', {:cwd => '/my/deploy/dir', :returns => [0,128]}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.find_current_revision).to eql("9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13") end it "gives the current revision as nil when there is no current revision" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true) @stderr = "fatal: Not a git repository (or any of the parent directories): .git" @stdout = "" expect(@provider).to receive(:shell_out!).with('git rev-parse HEAD', :cwd => '/my/deploy/dir', :returns => [0,128]).and_return(double("ShellOut result", :stdout => "", :stderr => @stderr)) expect(@provider.find_current_revision).to be_nil end end it "creates a current_resource with the currently deployed revision when a clone exists in the destination dir" do allow(@provider).to receive(:find_current_revision).and_return("681c9802d1c62a45b490786c18f0b8216b309440") @provider.load_current_resource expect(@provider.current_resource.name).to eql(@resource.name) expect(@provider.current_resource.revision).to eql("681c9802d1c62a45b490786c18f0b8216b309440") end it "keeps the node and resource passed to it on initialize" do expect(@provider.node).to equal(@node) expect(@provider.new_resource).to equal(@resource) end context "resolving revisions to a SHA" do before do @git_ls_remote = "git ls-remote \"git://github.com/opscode/chef.git\" " end it "returns resource.revision as is if revision is already a full SHA" do expect(@provider.target_revision).to eql("d35af14d41ae22b19da05d7d03a0bafc321b244c") end it "converts resource.revision from a tag to a SHA" do @resource.revision "v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from an annotated tag to the tagged SHA (not SHA of tag)" do @resource.revision "v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0^{}\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("663c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from a tag to a SHA using an exact match" do @resource.revision "v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/releases/v1.0\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from a tag to a SHA, matching tags first, then heads" do @resource.revision "v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("663c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from a tag to a SHA, matching heads if no tags match" do @resource.revision "v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.1\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from a tag to a SHA, matching tags first, then heads, then revision" do @resource.revision "refs/pulls/v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" + "805c22a5e41f5ae3193460cca044ed1435029f53\trefs/pulls/v1.0\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/pulls/v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("805c22a5e41f5ae3193460cca044ed1435029f53") end it "converts resource.revision from a tag to a SHA, using full path if provided" do @resource.revision "refs/heads/v1.0" @stdout = ("d03c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" + "663c22a5e41f5ae3193460cca044ed1435029f53\trefs/tags/v1.0\n" + "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/v1.0\n") expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"refs/heads/v1.0*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("503c22a5e41f5ae3193460cca044ed1435029f53") end it "raises an invalid remote reference error if you try to deploy from ``origin'' and assertions are run" do @resource.revision "origin/" @provider.action = :checkout @provider.define_resource_requirements allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect {@provider.process_resource_requirements}.to raise_error(Chef::Exceptions::InvalidRemoteGitReference) end it "raises an unresolvable git reference error if the revision can't be resolved to any revision and assertions are run" do @resource.revision "FAIL, that's the revision I want" @provider.action = :checkout expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", :stdout => "\n")) @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::UnresolvableGitReference) end it "does not raise an error if the revision can't be resolved when assertions are not run" do @resource.revision "FAIL, that's the revision I want" expect(@provider).to receive(:shell_out!).and_return(double("ShellOut result", :stdout => "\n")) expect(@provider.target_revision).to eq(nil) end it "does not raise an error when the revision is valid and assertions are run." do @resource.revision "0.8-alpha" @stdout = "503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha\n" expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"0.8-alpha*\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) @provider.action = :checkout allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) @provider.define_resource_requirements expect { @provider.process_resource_requirements }.not_to raise_error end it "gives the latest HEAD revision SHA if nothing is specified" do @stdout =<<-SHAS 28af684d8460ba4793eda3e7ac238c864a5d029a\tHEAD 503c22a5e41f5ae3193460cca044ed1435029f53\trefs/heads/0.8-alpha 28af684d8460ba4793eda3e7ac238c864a5d029a\trefs/heads/master c44fe79bb5e36941ce799cee6b9de3a2ef89afee\trefs/tags/0.5.2 14534f0e0bf133dc9ff6dbe74f8a0c863ff3ac6d\trefs/tags/0.5.4 d36fddb4291341a1ff2ecc3c560494e398881354\trefs/tags/0.5.6 9e5ce9031cbee81015de680d010b603bce2dd15f\trefs/tags/0.6.0 9b4d8dc38dd471246e7cfb1c3c1ad14b0f2bee13\trefs/tags/0.6.2 014a69af1cdce619de82afaf6cdb4e6ac658fede\trefs/tags/0.7.0 fa8097ff666af3ce64761d8e1f1c2aa292a11378\trefs/tags/0.7.2 44f9be0b33ba5c10027ddb030a5b2f0faa3eeb8d\trefs/tags/0.7.4 d7b9957f67236fa54e660cc3ab45ffecd6e0ba38\trefs/tags/0.7.8 b7d19519a1c15f1c1a324e2683bd728b6198ce5a\trefs/tags/0.7.8^{} ebc1b392fe7e8f0fbabc305c299b4d365d2b4d9b\trefs/tags/chef-server-package SHAS @resource.revision '' expect(@provider).to receive(:shell_out!).with(@git_ls_remote + "\"HEAD\"", {:log_tag=>"git[web2.0 app]"}).and_return(double("ShellOut result", :stdout => @stdout)) expect(@provider.target_revision).to eql("28af684d8460ba4793eda3e7ac238c864a5d029a") end end it "responds to :revision_slug as an alias for target_revision" do expect(@provider).to respond_to(:revision_slug) end context "with an ssh wrapper" do let(:deploy_user) { "deployNinja" } let(:wrapper) { "do_it_this_way.sh" } let(:expected_cmd) { 'git clone "git://github.com/opscode/chef.git" "/my/deploy/dir"' } let(:default_options) do { :user => deploy_user, :environment => { "GIT_SSH" => wrapper, "HOME" => "/home/deployNinja" }, :log_tag => "git[web2.0 app]" } end before do @resource.user deploy_user @resource.ssh_wrapper wrapper allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/deployNinja")) end context "without a timeout set" do it "clones a repo with default git options" do expect(@provider).to receive(:shell_out!).with(expected_cmd, default_options) @provider.clone end end context "with a timeout set" do let (:seconds) { 10 } before { @resource.timeout(seconds) } it "clones a repo with amended git options" do expect(@provider).to receive(:shell_out!).with(expected_cmd, default_options.merge(:timeout => seconds)) @provider.clone end end context "with a specific home" do let (:override_home) do {"HOME" => "/home/masterNinja"} end let(:overrided_options) do { :user => deploy_user, :environment => { "GIT_SSH" => wrapper, "HOME" => "/home/masterNinja" }, :log_tag => "git[web2.0 app]" } end before do @resource.environment(override_home) end before { @resource.environment(override_home) } it "clones a repo with amended git options with specific home" do expect(@provider).to receive(:shell_out!).with(expected_cmd, overrided_options) @provider.clone end end end it "runs a clone command with escaped destination" do @resource.user "deployNinja" allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/deployNinja")) @resource.destination "/Application Support/with/space" @resource.ssh_wrapper "do_it_this_way.sh" expected_cmd = "git clone \"git://github.com/opscode/chef.git\" \"/Application Support/with/space\"" expect(@provider).to receive(:shell_out!).with(expected_cmd, :user => "deployNinja", :environment =>{"GIT_SSH"=>"do_it_this_way.sh", "HOME" => "/home/deployNinja"}, :log_tag => "git[web2.0 app]") @provider.clone end it "compiles a clone command using --depth for shallow cloning" do @resource.depth 5 expected_cmd = "git clone --depth 5 \"git://github.com/opscode/chef.git\" \"/my/deploy/dir\"" version_response = double('shell_out') allow(version_response).to receive(:stdout) { 'git version 1.7.9' } expect(@provider).to receive(:shell_out!).with("git --version", :log_tag => "git[web2.0 app]").and_return(version_response) expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]") @provider.clone end it "compiles a clone command using --no-single-branch for shallow cloning when git >= 1.7.10" do @resource.depth 5 expected_cmd = "git clone --depth 5 --no-single-branch \"git://github.com/opscode/chef.git\" \"/my/deploy/dir\"" version_response = double('shell_out') allow(version_response).to receive(:stdout) { 'git version 1.7.10' } expect(@provider).to receive(:shell_out!).with("git --version", :log_tag => "git[web2.0 app]").and_return(version_response) expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]") @provider.clone end it "compiles a clone command with a remote other than ``origin''" do @resource.remote "opscode" expected_cmd = "git clone -o opscode \"git://github.com/opscode/chef.git\" \"/my/deploy/dir\"" expect(@provider).to receive(:shell_out!).with(expected_cmd, :log_tag => "git[web2.0 app]") @provider.clone end it "runs a checkout command with default options" do expect(@provider).to receive(:shell_out!).with('git branch -f deploy d35af14d41ae22b19da05d7d03a0bafc321b244c', :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]").ordered expect(@provider).to receive(:shell_out!).with('git checkout deploy', :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]").ordered @provider.checkout end it "runs an enable_submodule command" do @resource.enable_submodules true expected_cmd = "git submodule sync" expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") expected_cmd = "git submodule update --init --recursive" expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.enable_submodules end it "does nothing for enable_submodules if resource.enable_submodules #=> false" do expect(@provider).not_to receive(:shell_out!) @provider.enable_submodules end it "runs a sync command with default options" do expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository) expected_cmd = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c" expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd=> "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.fetch_updates end it "runs a sync command with the user and group specified in the resource" do @resource.user("whois") allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/whois")) @resource.group("thisis") expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository) expected_cmd = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c" expect(@provider).to receive(:shell_out!).with(expected_cmd, :cwd => "/my/deploy/dir", :user => "whois", :group => "thisis", :log_tag => "git[web2.0 app]", :environment=>{"HOME"=>"/home/whois"}) @provider.fetch_updates end it "configures remote tracking branches when remote is ``origin''" do @resource.remote "origin" expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository) fetch_command = "git fetch origin && git fetch origin --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c" expect(@provider).to receive(:shell_out!).with(fetch_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.fetch_updates end it "configures remote tracking branches when remote is not ``origin''" do @resource.remote "opscode" expect(@provider).to receive(:setup_remote_tracking_branches).with(@resource.remote, @resource.repository) fetch_command = "git fetch opscode && git fetch opscode --tags && git reset --hard d35af14d41ae22b19da05d7d03a0bafc321b244c" expect(@provider).to receive(:shell_out!).with(fetch_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.fetch_updates end context "configuring remote tracking branches" do it "checks if a remote with this name already exists" do command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 1 } expected_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(expected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :returns => [0,1,2]).and_return(command_response) add_remote_command = "git remote add #{@resource.remote} #{@resource.repository}" expect(@provider).to receive(:shell_out!).with(add_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end it "runs the config with the user and group specified in the resource" do @resource.user("whois") @resource.group("thisis") allow(Etc).to receive(:getpwnam).and_return(double("Struct::Passwd", :name => @resource.user, :dir => "/home/whois")) command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 1 } expected_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(expected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :user => "whois", :group => "thisis", :environment=>{"HOME"=>"/home/whois"}, :returns => [0,1,2]).and_return(command_response) add_remote_command = "git remote add #{@resource.remote} #{@resource.repository}" expect(@provider).to receive(:shell_out!).with(add_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :user => "whois", :group => "thisis", :environment=>{"HOME"=>"/home/whois"}) @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end describe "when a remote with a given name hasn't been configured yet" do it "adds a new remote " do command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 1 } check_remote_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(check_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :returns => [0,1,2]).and_return(command_response) expected_command = "git remote add #{@resource.remote} #{@resource.repository}" expect(@provider).to receive(:shell_out!).with(expected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end end describe "when a remote with a given name has already been configured" do it "updates remote url when the url is different" do command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 0 } allow(command_response).to receive(:stdout) { "some_other_url" } check_remote_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(check_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :returns => [0,1,2]).and_return(command_response) expected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}" expect(@provider).to receive(:shell_out!).with(expected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end it "doesn't update remote url when the url is the same" do command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 0 } allow(command_response).to receive(:stdout) { @resource.repository } check_remote_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(check_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :returns => [0,1,2]).and_return(command_response) unexpected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}" expect(@provider).not_to receive(:shell_out!).with(unexpected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end it "resets remote url when it has multiple values" do command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 2 } check_remote_command = "git config --get remote.#{@resource.remote}.url" expect(@provider).to receive(:shell_out!).with(check_remote_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]", :returns => [0,1,2]).and_return(command_response) expected_command = "git config --replace-all remote.#{@resource.remote}.url #{@resource.repository}" expect(@provider).to receive(:shell_out!).with(expected_command, :cwd => "/my/deploy/dir", :log_tag => "git[web2.0 app]") @provider.setup_remote_tracking_branches(@resource.remote, @resource.repository) end end end it "raises an error if the git clone command would fail because the enclosing directory doesn't exist" do allow(@provider).to receive(:shell_out!) expect {@provider.run_action(:sync)}.to raise_error(Chef::Exceptions::MissingParentDirectory) end it "does a checkout by cloning the repo and then enabling submodules" do # will be invoked in load_current_resource allow(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(false) allow(::File).to receive(:exist?).with("/my/deploy/dir").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::Dir).to receive(:entries).with("/my/deploy/dir").and_return(['.','..']) expect(@provider).to receive(:clone) expect(@provider).to receive(:checkout) expect(@provider).to receive(:enable_submodules) @provider.run_action(:checkout) # Even though an actual run will cause an update to occur, the fact that we've stubbed out # the actions above will prevent updates from registering # @resource.should be_updated end it "does not call checkout if enable_checkout is false" do # will be invoked in load_current_resource allow(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(false) allow(::File).to receive(:exist?).with("/my/deploy/dir").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::Dir).to receive(:entries).with("/my/deploy/dir").and_return(['.','..']) @resource.enable_checkout false expect(@provider).to receive(:clone) expect(@provider).not_to receive(:checkout) expect(@provider).to receive(:enable_submodules) @provider.run_action(:checkout) end # REGRESSION TEST: on some OSes, the entries from an empty directory will be listed as # ['..', '.'] but this shouldn't change the behavior it "does a checkout by cloning the repo and then enabling submodules when the directory entries are listed as %w{.. .}" do allow(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(false) allow(::File).to receive(:exist?).with("/my/deploy/dir").and_return(false) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::Dir).to receive(:entries).with("/my/deploy/dir").and_return(['..','.']) expect(@provider).to receive(:clone) expect(@provider).to receive(:checkout) expect(@provider).to receive(:enable_submodules) expect(@provider).to receive(:add_remotes) @provider.run_action(:checkout) # @resource.should be_updated end it "should not checkout if the destination exists or is a non empty directory" do # will be invoked in load_current_resource allow(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(false) allow(::File).to receive(:exist?).with("/my/deploy/dir").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::Dir).to receive(:entries).with("/my/deploy/dir").and_return(['.','..','foo','bar']) expect(@provider).not_to receive(:clone) expect(@provider).not_to receive(:checkout) expect(@provider).not_to receive(:enable_submodules) expect(@provider).not_to receive(:add_remotes) @provider.run_action(:checkout) expect(@resource).not_to be_updated end it "syncs the code by updating the source when the repo has already been checked out" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(@provider).to receive(:find_current_revision).exactly(1).and_return('d35af14d41ae22b19da05d7d03a0bafc321b244c') expect(@provider).not_to receive(:fetch_updates) expect(@provider).to receive(:add_remotes) @provider.run_action(:sync) expect(@resource).not_to be_updated end it "marks the resource as updated when the repo is updated and gets a new version" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) # invoked twice - first time from load_current_resource expect(@provider).to receive(:find_current_revision).exactly(1).and_return('d35af14d41ae22b19da05d7d03a0bafc321b244c') allow(@provider).to receive(:target_revision).and_return('28af684d8460ba4793eda3e7ac238c864a5d029a') expect(@provider).to receive(:fetch_updates) expect(@provider).to receive(:enable_submodules) expect(@provider).to receive(:add_remotes) @provider.run_action(:sync) # @resource.should be_updated end it "does not fetch any updates if the remote revision matches the current revision" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(@provider).to receive(:find_current_revision).and_return('d35af14d41ae22b19da05d7d03a0bafc321b244c') allow(@provider).to receive(:target_revision).and_return('d35af14d41ae22b19da05d7d03a0bafc321b244c') expect(@provider).not_to receive(:fetch_updates) expect(@provider).to receive(:add_remotes) @provider.run_action(:sync) expect(@resource).not_to be_updated end it "clones the repo instead of fetching it if the deploy directory doesn't exist" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").exactly(2).and_return(false) expect(@provider).to receive(:action_checkout) expect(@provider).not_to receive(:shell_out!) @provider.run_action(:sync) # @resource.should be_updated end it "clones the repo instead of fetching updates if the deploy directory is empty" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.git").exactly(2).and_return(false) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true) allow(@provider).to receive(:sync_command).and_return("huzzah!") expect(@provider).to receive(:action_checkout) expect(@provider).not_to receive(:shell_out!).with("huzzah!", :cwd => "/my/deploy/dir") @provider.run_action(:sync) #@resource.should be_updated end it "does an export by cloning the repo then removing the .git directory" do expect(@provider).to receive(:action_checkout) expect(FileUtils).to receive(:rm_rf).with(@resource.destination + "/.git") @provider.run_action(:export) expect(@resource).to be_updated end describe "calling add_remotes" do it "adds a new remote for each entry in additional remotes hash" do @resource.additional_remotes({:opscode => "opscode_repo_url", :another_repo => "some_other_repo_url"}) allow(STDOUT).to receive(:tty?).and_return(false) command_response = double('shell_out') allow(command_response).to receive(:exitstatus) { 0 } @resource.additional_remotes.each_pair do |remote_name, remote_url| expect(@provider).to receive(:setup_remote_tracking_branches).with(remote_name, remote_url) end @provider.add_remotes end end describe "calling multiple_remotes?" do before(:each) do @command_response = double('shell_out') end describe "when check remote command returns with status 2" do it "returns true" do allow(@command_response).to receive(:exitstatus) { 2 } expect(@provider.multiple_remotes?(@command_response)).to be_truthy end end describe "when check remote command returns with status 0" do it "returns false" do allow(@command_response).to receive(:exitstatus) { 0 } expect(@provider.multiple_remotes?(@command_response)).to be_falsey end end describe "when check remote command returns with status 0" do it "returns false" do allow(@command_response).to receive(:exitstatus) { 1 } expect(@provider.multiple_remotes?(@command_response)).to be_falsey end end end describe "calling remote_matches?" do before(:each) do @command_response = double('shell_out') end describe "when output of the check remote command matches the repository url" do it "returns true" do allow(@command_response).to receive(:exitstatus) { 0 } allow(@command_response).to receive(:stdout) { @resource.repository } expect(@provider.remote_matches?(@resource.repository, @command_response)).to be_truthy end end describe "when output of the check remote command doesn't match the repository url" do it "returns false" do allow(@command_response).to receive(:exitstatus) { 0 } allow(@command_response).to receive(:stdout) { @resource.repository + "test" } expect(@provider.remote_matches?(@resource.repository, @command_response)).to be_falsey end end end end chef-12.3.0/spec/unit/provider/dsc_script_spec.rb0000644000004100000410000002002212520074675021775 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/dsc/resource_info' require 'spec_helper' describe Chef::Provider::DscScript do context 'when DSC is available' do let (:node) { node = Chef::Node.new node.automatic[:languages][:powershell][:version] = '4.0' node } let (:events) { Chef::EventDispatch::Dispatcher.new } let (:run_context) { Chef::RunContext.new(node, {}, events) } let (:resource) { Chef::Resource::DscScript.new("script", run_context) } let (:provider) do Chef::Provider::DscScript.new(resource, run_context) end describe '#load_current_resource' do it "describes the resource as converged if there were 0 DSC resources" do allow(provider).to receive(:run_configuration).with(:test).and_return([]) provider.load_current_resource expect(provider.instance_variable_get('@resource_converged')).to be_truthy end it "describes the resource as not converged if there is 1 DSC resources that is converged" do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) provider.load_current_resource expect(provider.instance_variable_get('@resource_converged')).to be_truthy end it "describes the resource as not converged if there is 1 DSC resources that is not converged" do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) provider.load_current_resource expect(provider.instance_variable_get('@resource_converged')).to be_falsey end it "describes the resource as not converged if there are any DSC resources that are not converged" do dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) provider.load_current_resource expect(provider.instance_variable_get('@resource_converged')).to be_falsey end it "describes the resource as converged if all DSC resources that are converged" do dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) provider.load_current_resource expect(provider.instance_variable_get('@resource_converged')).to be_truthy end end describe '#generate_configuration_document' do # I think integration tests should cover these cases it 'uses configuration_document_from_script_path when a dsc script file is given' do allow(provider).to receive(:load_current_resource) resource.command("path_to_script") generator = double('Chef::Util::DSC::ConfigurationGenerator') expect(generator).to receive(:configuration_document_from_script_path) allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) provider.send(:generate_configuration_document, 'tmp', nil) end it 'uses configuration_document_from_script_code when a the dsc resource is given' do allow(provider).to receive(:load_current_resource) resource.code("ImADSCResource{}") generator = double('Chef::Util::DSC::ConfigurationGenerator') expect(generator).to receive(:configuration_document_from_script_code) allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) provider.send(:generate_configuration_document, 'tmp', nil) end it 'should noop if neither code or command are provided' do allow(provider).to receive(:load_current_resource) generator = double('Chef::Util::DSC::ConfigurationGenerator') expect(generator).to receive(:configuration_document_from_script_code).with('', anything(), anything(), anything()) allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) provider.send(:generate_configuration_document, 'tmp', nil) end end describe 'action_run' do it 'should converge the script if it is not converged' do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) allow(provider).to receive(:run_configuration).with(:set) provider.run_action(:run) expect(resource).to be_updated end it 'should not converge if the script is already converged' do allow(provider).to receive(:run_configuration).with(:test).and_return([]) provider.run_action(:run) expect(resource).not_to be_updated end end describe '#generate_description' do it 'removes the resource name from the beginning of any log line from the LCM' do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) expect(provider.send(:generate_description)[1]).to match(/converge DSC resource resourcename by doing something/) end it 'ignores the last line' do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) expect(provider.send(:generate_description)[1]).not_to match(/lastline/) end it 'reports a dsc resource has not been changed if the LCM reported no change was required' do dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', false, ['resourcename does nothing', 'lastline']) provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) expect(provider.send(:generate_description)[1]).to match(/converge DSC resource resourcename by doing nothing/) end end end context 'when Dsc is not available' do let (:node) { Chef::Node.new } let (:events) { Chef::EventDispatch::Dispatcher.new } let (:run_context) { Chef::RunContext.new(node, {}, events) } let (:resource) { Chef::Resource::DscScript.new('script', run_context) } let (:provider) { Chef::Provider::DscScript.new(resource, run_context) } describe 'action_run' do ['1.0', '2.0', '3.0'].each do |version| it "raises an exception for powershell version '#{version}'" do node.automatic[:languages][:powershell][:version] = version expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::NoProviderAvailable) end end it 'raises an exception if Powershell is not present' do expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::NoProviderAvailable) end end end end chef-12.3.0/spec/unit/provider/whyrun_safe_ruby_block_spec.rb0000644000004100000410000000320512520074675024411 0ustar www-datawww-data# # Author:: Phil Dibowitz () # Copyright:: Copyright (c) 2013 Facebook # 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::WhyrunSafeRubyBlock, "initialize" do before(:each) do $evil_global_evil_laugh = :wahwah @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::WhyrunSafeRubyBlock.new("bloc party") @new_resource.block { $evil_global_evil_laugh = :mwahahaha} @provider = Chef::Provider::WhyrunSafeRubyBlock.new(@new_resource, @run_context) end it "should call the block and flag the resource as updated" do @provider.run_action(:run) expect($evil_global_evil_laugh).to eq(:mwahahaha) expect(@new_resource).to be_updated end it "should call the block and flat the resource as updated - even in whyrun" do Chef::Config[:why_run] = true @provider.run_action(:run) expect($evil_global_evil_laugh).to eq(:mwahahaha) expect(@new_resource).to be_updated Chef::Config[:why_run] = false end end chef-12.3.0/spec/unit/provider/group_spec.rb0000644000004100000410000002604012520074675021002 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::User do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("wheel", @run_context) @new_resource.gid 500 @new_resource.members "aj" @provider = Chef::Provider::Group.new(@new_resource, @run_context) @current_resource = Chef::Resource::Group.new("aj", @run_context) @current_resource.gid 500 @current_resource.members "aj" @provider.current_resource = @current_resource @pw_group = double("Struct::Group", :name => "wheel", :gid => 20, :mem => [ "root", "aj" ] ) allow(Etc).to receive(:getgrnam).with('wheel').and_return(@pw_group) end it "assumes the group exists by default" do expect(@provider.group_exists).to be_truthy end describe "when establishing the current state of the group" do it "sets the group name of the current resource to the group name of the new resource" do @provider.load_current_resource expect(@provider.current_resource.group_name).to eq('wheel') end it "does not modify the desired gid if set" do @provider.load_current_resource expect(@new_resource.gid).to eq(500) end it "sets the desired gid to the current gid if none is set" do @new_resource.instance_variable_set(:@gid, nil) @provider.load_current_resource expect(@new_resource.gid).to eq(20) end it "looks up the group in /etc/group with getgrnam" do expect(Etc).to receive(:getgrnam).with(@new_resource.group_name).and_return(@pw_group) @provider.load_current_resource expect(@provider.current_resource.gid).to eq(20) expect(@provider.current_resource.members).to eq(%w{root aj}) end it "should flip the value of exists if it cannot be found in /etc/group" do allow(Etc).to receive(:getgrnam).and_raise(ArgumentError) @provider.load_current_resource expect(@provider.group_exists).to be_falsey end it "should return the current resource" do expect(@provider.load_current_resource).to equal(@provider.current_resource) end end describe "when determining if the system is already in the target state" do [ :gid, :members ].each do |attribute| it "should return true if #{attribute} doesn't match" do allow(@current_resource).to receive(attribute).and_return("looooooooooooooooooool") expect(@provider.compare_group).to be_truthy end end it "should return false if gid and members are equal" do expect(@provider.compare_group).to be_falsey end it "should coerce an integer to a string for comparison" do allow(@current_resource).to receive(:gid).and_return("500") expect(@provider.compare_group).to be_falsey end it "should return false if append is true and the group member(s) already exists" do @current_resource.members << "extra_user" allow(@new_resource).to receive(:append).and_return(true) expect(@provider.compare_group).to be_falsey end it "should return true if append is true and the group member(s) do not already exist" do @new_resource.members << "extra_user" allow(@new_resource).to receive(:append).and_return(true) expect(@provider.compare_group).to be_truthy end it "should return false if append is true and excluded_members include a non existing member" do @new_resource.excluded_members << "extra_user" allow(@new_resource).to receive(:append).and_return(true) expect(@provider.compare_group).to be_falsey end it "should return true if the append is true and excluded_members include an existing user" do @new_resource.members.each {|m| @new_resource.excluded_members << m } @new_resource.members.clear allow(@new_resource).to receive(:append).and_return(true) expect(@provider.compare_group).to be_truthy end end describe "when creating a group" do it "should call create_group if the group does not exist" do @provider.group_exists = false expect(@provider).to receive(:create_group).and_return(true) @provider.run_action(:create) end it "should set the new_resources updated flag when it creates the group" do @provider.group_exists = false allow(@provider).to receive(:create_group) @provider.run_action(:create) expect(@provider.new_resource).to be_updated end it "should check to see if the group has mismatched attributes if the group exists" do @provider.group_exists = true allow(@provider).to receive(:compare_group).and_return(false) allow(@provider).to receive(:change_desc).and_return([ ]) @provider.run_action(:create) expect(@provider.new_resource).not_to be_updated end it "should call manage_group if the group exists and has mismatched attributes" do @provider.group_exists = true allow(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return([ ]) expect(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:create) end it "should set the new_resources updated flag when it creates the group if we call manage_group" do @provider.group_exists = true allow(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) allow(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:create) expect(@new_resource).to be_updated end end describe "when removing a group" do it "should not call remove_group if the group does not exist" do @provider.group_exists = false expect(@provider).not_to receive(:remove_group) @provider.run_action(:remove) expect(@provider.new_resource).not_to be_updated end it "should call remove_group if the group exists" do @provider.group_exists = true expect(@provider).to receive(:remove_group) @provider.run_action(:remove) expect(@provider.new_resource).to be_updated end end describe "when updating a group" do before(:each) do @provider.group_exists = true allow(@provider).to receive(:manage_group).and_return(true) end it "should run manage_group if the group exists and has mismatched attributes" do expect(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) expect(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:manage) end it "should set the new resources updated flag to true if manage_group is called" do allow(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) allow(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:manage) expect(@new_resource).to be_updated end it "should not run manage_group if the group does not exist" do @provider.group_exists = false expect(@provider).not_to receive(:manage_group) @provider.run_action(:manage) end it "should not run manage_group if the group exists but has no differing attributes" do expect(@provider).to receive(:compare_group).and_return(false) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) expect(@provider).not_to receive(:manage_group) @provider.run_action(:manage) end end describe "when modifying the group" do before(:each) do @provider.group_exists = true allow(@provider).to receive(:manage_group).and_return(true) end it "should run manage_group if the group exists and has mismatched attributes" do expect(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) expect(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:modify) end it "should set the new resources updated flag to true if manage_group is called" do allow(@provider).to receive(:compare_group).and_return(true) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) allow(@provider).to receive(:manage_group).and_return(true) @provider.run_action(:modify) expect(@new_resource).to be_updated end it "should not run manage_group if the group exists but has no differing attributes" do expect(@provider).to receive(:compare_group).and_return(false) allow(@provider).to receive(:change_desc).and_return(["Some changes are going to be done."]) expect(@provider).not_to receive(:manage_group) @provider.run_action(:modify) end it "should raise a Chef::Exceptions::Group if the group doesn't exist" do @provider.group_exists = false expect { @provider.run_action(:modify) }.to raise_error(Chef::Exceptions::Group) end end describe "when determining the reason for a change" do it "should report which group members are missing if members are missing and appending to the group" do @new_resource.members << "user1" @new_resource.members << "user2" allow(@new_resource).to receive(:append).and_return true expect(@provider.compare_group).to be_truthy expect(@provider.change_desc).to eq([ "add missing member(s): user1, user2" ]) end it "should report that the group members will be overwritten if not appending" do @new_resource.members << "user1" allow(@new_resource).to receive(:append).and_return false expect(@provider.compare_group).to be_truthy expect(@provider.change_desc).to eq([ "replace group members with new list of members" ]) end it "should report the gid will be changed when it does not match" do allow(@current_resource).to receive(:gid).and_return("BADF00D") expect(@provider.compare_group).to be_truthy expect(@provider.change_desc).to eq([ "change gid #{@current_resource.gid} to #{@new_resource.gid}" ]) end it "should report no change reason when no change is required" do expect(@provider.compare_group).to be_falsey expect(@provider.change_desc).to eq([ ]) end end end chef-12.3.0/spec/unit/provider/package_spec.rbe0000644000004100000410000000000012520074675021372 0ustar www-datawww-datachef-12.3.0/spec/unit/provider/file/0000755000004100000410000000000012520074675017224 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/file/content_spec.rb0000644000004100000410000000726412520074675022246 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::File::Content do # # mock setup # let(:current_resource) do double("Chef::Provider::File::Resource (current)") end let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } let(:new_resource) do double("Chef::Provider::File::Resource (new)", :name => "seattle.txt", :path => resource_path) end let(:run_context) do double("Chef::RunContext") end # # subject # let(:content) do Chef::Provider::File::Content.new(new_resource, current_resource, run_context) end describe "when the resource has a content attribute set" do before do allow(new_resource).to receive(:content).and_return("Do do do do, do do do do, do do do do, do do do do") end it "returns a tempfile" do expect(content.tempfile).to be_a_kind_of(Tempfile) end it "the tempfile contents should match the resource contents" do expect(IO.read(content.tempfile.path)).to eq(new_resource.content) end it "returns a tempfile in the tempdir when :file_staging_uses_destdir is not set" do Chef::Config[:file_staging_uses_destdir] = false expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_truthy expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey end it "returns a tempfile in the destdir when :file_deployment_uses_destdir is set" do Chef::Config[:file_staging_uses_destdir] = true expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_falsey expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_truthy end context "when creating a tempfiles in destdir fails" do let(:enclosing_directory) { canonicalize_path("/nonexisting/path") } it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do Chef::Config[:file_staging_uses_destdir] = :auto expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_truthy expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey end it "fails when :file_desployment_uses_destdir is set" do Chef::Config[:file_staging_uses_destdir] = true expect{content.tempfile}.to raise_error end it "returns a tempfile in the tempdir when :file_desployment_uses_destdir is not set" do expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be_truthy expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be_falsey end end end describe "when the resource does not have a content attribute set" do before do allow(new_resource).to receive(:content).and_return(nil) end it "should return nil instead of a tempfile" do expect(content.tempfile).to be_nil end end end chef-12.3.0/spec/unit/provider/cron_spec.rb0000644000004100000410000007323112520074675020613 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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::Cron do describe "when with special time string" do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Cron.new("cronhole some stuff", @run_context) @new_resource.user "root" @new_resource.minute "30" @new_resource.command "/bin/true" @new_resource.time :reboot @provider = Chef::Provider::Cron.new(@new_resource, @run_context) end context "with a matching entry in the user's crontab" do before :each do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff @reboot /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should set cron_exists" do @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(@provider.cron_empty).to eq(false) end it "should pull the details out of the cron line" do cron = @provider.load_current_resource expect(cron.time).to eq(:reboot) expect(cron.command).to eq('/bin/true param1 param2') end it "should pull env vars out" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com SHELL=/bin/foosh PATH=/bin:/foo HOME=/home/foo @reboot /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB cron = @provider.load_current_resource expect(cron.mailto).to eq('foo@example.com') expect(cron.shell).to eq('/bin/foosh') expect(cron.path).to eq('/bin:/foo') expect(cron.home).to eq('/home/foo') expect(cron.time).to eq(:reboot) expect(cron.command).to eq('/bin/true param1 param2') end it "should parse and load generic and standard environment variables from cron entry" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) # Chef Name: cronhole some stuff MAILTO=warn@example.com TEST=lol FLAG=1 @reboot /bin/true CRONTAB cron = @provider.load_current_resource expect(cron.mailto).to eq("warn@example.com") expect(cron.environment).to eq({"TEST" => "lol", "FLAG" => "1"}) end it "should not break with variables that match the cron resource internals" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) # Chef Name: cronhole some stuff MINUTE=40 REBOOT=midnight TEST=lol ENVIRONMENT=production @reboot /bin/true CRONTAB cron = @provider.load_current_resource expect(cron.time).to eq(:reboot) expect(cron.environment).to eq({"MINUTE" => "40", "REBOOT" => "midnight", "TEST" => "lol", "ENVIRONMENT" => "production"}) end it "should report the match" do expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'") @provider.load_current_resource end describe "action_create" do before :each do allow(@provider).to receive(:write_crontab) allow(@provider).to receive(:read_crontab).and_return(nil) end context "when there is no existing crontab" do before :each do @provider.cron_exists = false @provider.cron_empty = true end it "should create a crontab with the entry" do expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) # Chef Name: cronhole some stuff @reboot /bin/true ENDCRON @provider.run_action(:create) end end end end end before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Cron.new("cronhole some stuff", @run_context) @new_resource.user "root" @new_resource.minute "30" @new_resource.command "/bin/true" @provider = Chef::Provider::Cron.new(@new_resource, @run_context) end describe "when examining the current system state" do context "with no crontab for the user" do before :each do allow(@provider).to receive(:read_crontab).and_return(nil) end it "should set cron_empty" do @provider.load_current_resource expect(@provider.cron_empty).to eq(true) expect(@provider.cron_exists).to eq(false) end it "should report an empty crontab" do expect(Chef::Log).to receive(:debug).with("Cron empty for '#{@new_resource.user}'") @provider.load_current_resource end end context "with no matching entry in the user's crontab" do before :each do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: something else * 5 * * * /bin/true # Another comment CRONTAB end it "should not set cron_exists or cron_empty" do @provider.load_current_resource expect(@provider.cron_exists).to eq(false) expect(@provider.cron_empty).to eq(false) end it "should report no entry found" do expect(Chef::Log).to receive(:debug).with("Cron '#{@new_resource.name}' not found") @provider.load_current_resource end it "should not fail if there's an existing cron with a numerical argument" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) # Chef Name: foo[bar] (baz) 21 */4 * * * some_prog 1234567 CRONTAB expect { @provider.load_current_resource }.not_to raise_error end end context "with a matching entry in the user's crontab" do before :each do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff * 5 * 1 * /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should set cron_exists" do @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(@provider.cron_empty).to eq(false) end it "should pull the details out of the cron line" do cron = @provider.load_current_resource expect(cron.minute).to eq('*') expect(cron.hour).to eq('5') expect(cron.day).to eq('*') expect(cron.month).to eq('1') expect(cron.weekday).to eq('*') expect(cron.time).to eq(nil) expect(cron.command).to eq('/bin/true param1 param2') end it "should pull env vars out" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com SHELL=/bin/foosh PATH=/bin:/foo HOME=/home/foo * 5 * 1 * /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB cron = @provider.load_current_resource expect(cron.mailto).to eq('foo@example.com') expect(cron.shell).to eq('/bin/foosh') expect(cron.path).to eq('/bin:/foo') expect(cron.home).to eq('/home/foo') expect(cron.minute).to eq('*') expect(cron.hour).to eq('5') expect(cron.day).to eq('*') expect(cron.month).to eq('1') expect(cron.weekday).to eq('*') expect(cron.time).to eq(nil) expect(cron.command).to eq('/bin/true param1 param2') end it "should parse and load generic and standard environment variables from cron entry" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) # Chef Name: cronhole some stuff MAILTO=warn@example.com TEST=lol FLAG=1 * 5 * * * /bin/true CRONTAB cron = @provider.load_current_resource expect(cron.mailto).to eq("warn@example.com") expect(cron.environment).to eq({"TEST" => "lol", "FLAG" => "1"}) end it "should not break with variabels that match the cron resource internals" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) # Chef Name: cronhole some stuff MINUTE=40 HOUR=midnight TEST=lol ENVIRONMENT=production * 5 * * * /bin/true CRONTAB cron = @provider.load_current_resource expect(cron.minute).to eq('*') expect(cron.hour).to eq('5') expect(cron.environment).to eq({"MINUTE" => "40", "HOUR" => "midnight", "TEST" => "lol", "ENVIRONMENT" => "production"}) end it "should report the match" do expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'") @provider.load_current_resource end end context "with a matching entry in the user's crontab using month names and weekday names (#CHEF-3178)" do before :each do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff * 5 * Jan Mon /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should set cron_exists" do @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(@provider.cron_empty).to eq(false) end it "should pull the details out of the cron line" do cron = @provider.load_current_resource expect(cron.minute).to eq('*') expect(cron.hour).to eq('5') expect(cron.day).to eq('*') expect(cron.month).to eq('Jan') expect(cron.weekday).to eq('Mon') expect(cron.command).to eq('/bin/true param1 param2') end it "should report the match" do expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'") @provider.load_current_resource end end context "with a matching entry without a crontab line" do it "should set cron_exists and leave current_resource values at defaults" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff CRONTAB cron = @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(cron.minute).to eq('*') expect(cron.hour).to eq('*') expect(cron.day).to eq('*') expect(cron.month).to eq('*') expect(cron.weekday).to eq('*') expect(cron.time).to eq(nil) expect(cron.command).to eq(nil) end it "should not pick up a commented out crontab line" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff #* 5 * 1 * /bin/true param1 param2 CRONTAB cron = @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(cron.minute).to eq('*') expect(cron.hour).to eq('*') expect(cron.day).to eq('*') expect(cron.month).to eq('*') expect(cron.weekday).to eq('*') expect(cron.time).to eq(nil) expect(cron.command).to eq(nil) end it "should not pick up a later crontab entry" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff #* 5 * 1 * /bin/true param1 param2 # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB cron = @provider.load_current_resource expect(@provider.cron_exists).to eq(true) expect(cron.minute).to eq('*') expect(cron.hour).to eq('*') expect(cron.day).to eq('*') expect(cron.month).to eq('*') expect(cron.weekday).to eq('*') expect(cron.time).to eq(nil) expect(cron.command).to eq(nil) end end end describe "cron_different?" do before :each do @current_resource = Chef::Resource::Cron.new("cronhole some stuff") @current_resource.user "root" @current_resource.minute "30" @current_resource.command "/bin/true" @provider.current_resource = @current_resource end [:minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home].each do |attribute| it "should return true if #{attribute} doesn't match" do @new_resource.send(attribute, "something_else") expect(@provider.cron_different?).to eql(true) end end it "should return true if special time string doesn't match" do @new_resource.send(:time, :reboot) expect(@provider.cron_different?).to eql(true) end it "should return true if environment doesn't match" do @new_resource.environment "FOO" => "something_else" expect(@provider.cron_different?).to eql(true) end it "should return true if mailto doesn't match" do @current_resource.mailto "foo@bar.com" @new_resource.mailto(nil) expect(@provider.cron_different?).to eql(true) end it "should return false if the objects are identical" do expect(@provider.cron_different?).to eq(false) end end describe "action_create" do before :each do allow(@provider).to receive(:write_crontab) allow(@provider).to receive(:read_crontab).and_return(nil) end context "when there is no existing crontab" do before :each do @provider.cron_exists = false @provider.cron_empty = true end it "should create a crontab with the entry" do expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) # Chef Name: cronhole some stuff 30 * * * * /bin/true ENDCRON @provider.run_action(:create) end it "should include env variables that are set" do @new_resource.mailto 'foo@example.com' @new_resource.path '/usr/bin:/my/custom/path' @new_resource.shell '/bin/foosh' @new_resource.home '/home/foo' @new_resource.environment "TEST" => "LOL" expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) # Chef Name: cronhole some stuff MAILTO=foo@example.com PATH=/usr/bin:/my/custom/path SHELL=/bin/foosh HOME=/home/foo TEST=LOL 30 * * * * /bin/true ENDCRON @provider.run_action(:create) end it "should mark the resource as updated" do @provider.run_action(:create) expect(@new_resource).to be_updated_by_last_action end it "should log the action" do expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] added crontab entry") @provider.run_action(:create) end end context "when there is a crontab with no matching section" do before :each do @provider.cron_exists = false allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should add the entry to the crontab" do expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: something else 2 * 1 * * /bin/false # Another comment # Chef Name: cronhole some stuff 30 * * * * /bin/true ENDCRON @provider.run_action(:create) end it "should include env variables that are set" do @new_resource.mailto 'foo@example.com' @new_resource.path '/usr/bin:/my/custom/path' @new_resource.shell '/bin/foosh' @new_resource.home '/home/foo' @new_resource.environment "TEST" => "LOL" expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: something else 2 * 1 * * /bin/false # Another comment # Chef Name: cronhole some stuff MAILTO=foo@example.com PATH=/usr/bin:/my/custom/path SHELL=/bin/foosh HOME=/home/foo TEST=LOL 30 * * * * /bin/true ENDCRON @provider.run_action(:create) end it "should mark the resource as updated" do @provider.run_action(:create) expect(@new_resource).to be_updated_by_last_action end it "should log the action" do expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] added crontab entry") @provider.run_action(:create) end end context "when there is a crontab with a matching but different section" do before :each do @provider.cron_exists = true allow(@provider).to receive(:cron_different?).and_return(true) allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff 30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should update the crontab entry" do expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff 30 * * * * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:create) end it "should include env variables that are set" do @new_resource.mailto 'foo@example.com' @new_resource.path '/usr/bin:/my/custom/path' @new_resource.shell '/bin/foosh' @new_resource.home '/home/foo' @new_resource.environment "TEST" => "LOL" expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com PATH=/usr/bin:/my/custom/path SHELL=/bin/foosh HOME=/home/foo TEST=LOL 30 * * * * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:create) end it "should mark the resource as updated" do @provider.run_action(:create) expect(@new_resource).to be_updated_by_last_action end it "should log the action" do expect(Chef::Log).to receive(:info).with("cron[cronhole some stuff] updated crontab entry") @provider.run_action(:create) end end context "when there is a crontab with a matching section with no crontab line in it" do before :each do @provider.cron_exists = true allow(@provider).to receive(:cron_different?).and_return(true) end it "should add the crontab to the entry" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff 30 * * * * /bin/true ENDCRON @provider.run_action(:create) end it "should not blat any following entries" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff #30 * * * * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff 30 * * * * /bin/true #30 * * * * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:create) end it "should handle env vars with no crontab" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=bar@example.com PATH=/usr/bin:/my/custom/path SHELL=/bin/barsh HOME=/home/foo # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB @new_resource.mailto 'foo@example.com' @new_resource.path '/usr/bin:/my/custom/path' @new_resource.shell '/bin/foosh' @new_resource.home '/home/foo' expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com PATH=/usr/bin:/my/custom/path SHELL=/bin/foosh HOME=/home/foo 30 * * * * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:create) end end context "when there is a crontab with a matching and identical section" do before :each do @provider.cron_exists = true allow(@provider).to receive(:cron_different?).and_return(false) allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff * 5 * * * /bin/true # Another comment CRONTAB end it "should not update the crontab" do expect(@provider).not_to receive(:write_crontab) @provider.run_action(:create) end it "should not mark the resource as updated" do @provider.run_action(:create) expect(@new_resource).not_to be_updated_by_last_action end it "should log nothing changed" do expect(Chef::Log).to receive(:debug).with("Found cron '#{@new_resource.name}'") expect(Chef::Log).to receive(:debug).with("Skipping existing cron entry '#{@new_resource.name}'") @provider.run_action(:create) end end end describe "action_delete" do before :each do allow(@provider).to receive(:write_crontab) allow(@provider).to receive(:read_crontab).and_return(nil) end context "when the user's crontab has no matching section" do before :each do @provider.cron_exists = false end it "should do nothing" do expect(@provider).not_to receive(:write_crontab) expect(Chef::Log).not_to receive(:info) @provider.run_action(:delete) end it "should not mark the resource as updated" do @provider.run_action(:delete) expect(@new_resource).not_to be_updated_by_last_action end end context "when the user has a crontab with a matching section" do before :each do @provider.cron_exists = true allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff 30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB end it "should remove the entry" do expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:delete) end it "should remove any env vars with the entry" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com FOO=test 30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:delete) end it "should mark the resource as updated" do @provider.run_action(:delete) expect(@new_resource).to be_updated_by_last_action end it "should log the action" do expect(Chef::Log).to receive(:info).with("#{@new_resource} deleted crontab entry") @provider.run_action(:delete) end end context "when the crontab has a matching section with no crontab line" do before :each do @provider.cron_exists = true end it "should remove the section" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command ENDCRON @provider.run_action(:delete) end it "should not blat following sections" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff #30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command #30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:delete) end it "should remove any envvars with the section" do allow(@provider).to receive(:read_crontab).and_return(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: cronhole some stuff MAILTO=foo@example.com #30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment CRONTAB expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) 0 2 * * * /some/other/command #30 * * 3 * /bin/true # Chef Name: something else 2 * 1 * * /bin/false # Another comment ENDCRON @provider.run_action(:delete) end end end describe "read_crontab" do before :each do @status = double("Status", :exitstatus => 0) @stdout = StringIO.new(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: something else * 5 * * * /bin/true # Another comment CRONTAB allow(@provider).to receive(:popen4).and_yield(1234, StringIO.new, @stdout, StringIO.new).and_return(@status) end it "should call crontab -l with the user" do expect(@provider).to receive(:popen4).with("crontab -l -u #{@new_resource.user}").and_return(@status) @provider.send(:read_crontab) end it "should return the contents of the crontab" do crontab = @provider.send(:read_crontab) expect(crontab).to eq <<-CRONTAB 0 2 * * * /some/other/command # Chef Name: something else * 5 * * * /bin/true # Another comment CRONTAB end it "should return nil if the user has no crontab" do status = double("Status", :exitstatus => 1) allow(@provider).to receive(:popen4).and_return(status) expect(@provider.send(:read_crontab)).to eq(nil) end it "should raise an exception if another error occurs" do status = double("Status", :exitstatus => 2) allow(@provider).to receive(:popen4).and_return(status) expect do @provider.send(:read_crontab) end.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: 2") end end describe "write_crontab" do before :each do @status = double("Status", :exitstatus => 0) @stdin = StringIO.new allow(@provider).to receive(:popen4).and_yield(1234, @stdin, StringIO.new, StringIO.new).and_return(@status) end it "should call crontab for the user" do expect(@provider).to receive(:popen4).with("crontab -u #{@new_resource.user} -", :waitlast => true).and_return(@status) @provider.send(:write_crontab, "Foo") end it "should write the given string to the crontab command" do @provider.send(:write_crontab, "Foo\n# wibble\n wah!!") expect(@stdin.string).to eq("Foo\n# wibble\n wah!!") end it "should raise an exception if the command returns non-zero" do allow(@status).to receive(:exitstatus).and_return(1) expect do @provider.send(:write_crontab, "Foo") end.to raise_error(Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: 1") end it "should raise an exception if the command die's and parent tries to write" do class WriteErrPipe def write(str) raise Errno::EPIPE, "Test" end end allow(@status).to receive(:exitstatus).and_return(1) allow(@provider).to receive(:popen4).and_yield(1234, WriteErrPipe.new, StringIO.new, StringIO.new).and_return(@status) expect(Chef::Log).to receive(:debug).with("Broken pipe - Test") expect do @provider.send(:write_crontab, "Foo") end.to raise_error(Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: 1") end end describe "weekday_in_crontab" do context "when weekday is symbol" do it "should return weekday in crontab format" do @new_resource.weekday :wednesday expect(@provider.send(:weekday_in_crontab)).to eq("3") end it "should raise an error with an unknown weekday" do expect { @new_resource.weekday :caturday }.to raise_error(RangeError) end end context "when weekday is a number in a string" do it "should return the string" do @new_resource.weekday "3" expect(@provider.send(:weekday_in_crontab)).to eq("3") end it "should raise an error with an out of range number" do expect { @new_resource.weekday "-1" }.to raise_error(RangeError) end end context "when weekday is string with the name of the week" do it "should return the string" do @new_resource.weekday "mon" expect(@provider.send(:weekday_in_crontab)).to eq("mon") end end context "when weekday is an integer" do it "should return the integer" do @new_resource.weekday 1 expect(@provider.send(:weekday_in_crontab)).to eq("1") end it "should raise an error with an out of range integer" do expect { @new_resource.weekday 45 }.to raise_error(RangeError) end end end end chef-12.3.0/spec/unit/provider/service_spec.rb0000644000004100000410000001426212520074675021311 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Service do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service.new(@new_resource, @run_context) @provider.current_resource = @current_resource allow(@provider).to receive(:load_current_resource) end describe "when enabling the service" do it "should enable the service if disabled and set the resource as updated" do @current_resource.enabled(false) expect(@provider).to receive(:enable_service).and_return(true) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).to be_updated end it "should not enable the service if already enabled" do @current_resource.enabled(true) expect(@provider).not_to receive(:enable_service) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).not_to be_updated end end describe "when disabling the service" do it "should disable the service if enabled and set the resource as updated" do allow(@current_resource).to receive(:enabled).and_return(true) expect(@provider).to receive(:disable_service).and_return(true) @provider.run_action(:disable) expect(@provider.new_resource).to be_updated end it "should not disable the service if already disabled" do allow(@current_resource).to receive(:enabled).and_return(false) expect(@provider).not_to receive(:disable_service) @provider.run_action(:disable) expect(@provider.new_resource).not_to be_updated end end describe "action_start" do it "should start the service if it isn't running and set the resource as updated" do @current_resource.running(false) expect(@provider).to receive(:start_service).with(no_args).and_return(true) @provider.run_action(:start) expect(@provider.new_resource).to be_updated end it "should not start the service if already running" do @current_resource.running(true) expect(@provider).not_to receive(:start_service) @provider.run_action(:start) expect(@provider.new_resource).not_to be_updated end end describe "action_stop" do it "should stop the service if it is running and set the resource as updated" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).to receive(:stop_service).and_return(true) @provider.run_action(:stop) expect(@provider.new_resource).to be_updated end it "should not stop the service if it's already stopped" do allow(@current_resource).to receive(:running).and_return(false) expect(@provider).not_to receive(:stop_service) @provider.run_action(:stop) expect(@provider.new_resource).not_to be_updated end end describe "action_restart" do before do @current_resource.supports(:restart => true) end it "should restart the service if it's supported and set the resource as updated" do expect(@provider).to receive(:restart_service).and_return(true) @provider.run_action(:restart) expect(@provider.new_resource).to be_updated end it "should restart the service even if it isn't running and set the resource as updated" do allow(@current_resource).to receive(:running).and_return(false) expect(@provider).to receive(:restart_service).and_return(true) @provider.run_action(:restart) expect(@provider.new_resource).to be_updated end end describe "action_reload" do before do @new_resource.supports(:reload => true) end it "should raise an exception if reload isn't supported" do @new_resource.supports(:reload => false) allow(@new_resource).to receive(:reload_command).and_return(false) expect { @provider.run_action(:reload) }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should reload the service if it is running and set the resource as updated" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).to receive(:reload_service).and_return(true) @provider.run_action(:reload) expect(@provider.new_resource).to be_updated end it "should not reload the service if it's stopped" do allow(@current_resource).to receive(:running).and_return(false) expect(@provider).not_to receive(:reload_service) @provider.run_action(:stop) expect(@provider.new_resource).not_to be_updated end end it "delegates enable_service to subclasses" do expect { @provider.enable_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "delegates disable_service to subclasses" do expect { @provider.disable_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "delegates start_service to subclasses" do expect { @provider.start_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "delegates stop_service to subclasses" do expect { @provider.stop_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "delegates restart_service to subclasses" do expect { @provider.restart_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "delegates reload_service to subclasses" do expect { @provider.reload_service }.to raise_error(Chef::Exceptions::UnsupportedAction) end end chef-12.3.0/spec/unit/provider/subversion_spec.rb0000644000004100000410000003242012520074675022044 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Subversion do before do @resource = Chef::Resource::Subversion.new("my app") @resource.repository "http://svn.example.org/trunk/" @resource.destination "/my/deploy/dir" @resource.revision "12345" @resource.svn_arguments(false) @resource.svn_info_args(false) @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @provider = Chef::Provider::Subversion.new(@resource, @run_context) end it "converts resource attributes to options for run_command and popen4" do expect(@provider.run_options).to eq({}) @resource.user 'deployninja' expect(@provider.run_options).to eq({:user => "deployninja"}) end context "determining the revision of the currently deployed code" do before do @stdout = double("stdout") @stderr = double("stderr") @exitstatus = double("exitstatus") end it "sets the revision to nil if there isn't any deployed code yet" do expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(false) expect(@provider.find_current_revision).to be_nil end it "determines the current revision if there's a checkout with svn data available" do example_svn_info = "Path: .\n" + "URL: http://svn.example.org/trunk/myapp\n" + "Repository Root: http://svn.example.org\n" + "Repository UUID: d62ff500-7bbc-012c-85f1-0026b0e37c24\n" + "Revision: 11739\nNode Kind: directory\n" + "Schedule: normal\n" + "Last Changed Author: codeninja\n" + "Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision "Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n" expect(::File).to receive(:exist?).at_least(1).times.with("/my/deploy/dir/.svn").and_return(true) expect(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true) expect(::Dir).to receive(:chdir).with("/my/deploy/dir").and_yield allow(@stdout).to receive(:string).and_return(example_svn_info) allow(@stderr).to receive(:string).and_return("") allow(@exitstatus).to receive(:exitstatus).and_return(0) expected_command = ["svn info", {:cwd=>"/my/deploy/dir"}] expect(@provider).to receive(:popen4).with(*expected_command). and_yield("no-pid", "no-stdin", @stdout,@stderr). and_return(@exitstatus) expect(@provider.find_current_revision).to eql("11410") end it "gives nil as the current revision if the deploy dir isn't a SVN working copy" do example_svn_info = "svn: '/tmp/deploydir' is not a working copy\n" expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(true) expect(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true) expect(::Dir).to receive(:chdir).with("/my/deploy/dir").and_yield allow(@stdout).to receive(:string).and_return(example_svn_info) allow(@stderr).to receive(:string).and_return("") allow(@exitstatus).to receive(:exitstatus).and_return(1) expect(@provider).to receive(:popen4).and_yield("no-pid", "no-stdin", @stdout,@stderr). and_return(@exitstatus) expect(@provider.find_current_revision).to be_nil end it "finds the current revision when loading the current resource state" do # note: the test is kinda janky, but it provides regression coverage for CHEF-2092 @resource.instance_variable_set(:@action, :sync) expect(@provider).to receive(:find_current_revision).and_return("12345") @provider.load_current_resource expect(@provider.current_resource.revision).to eq("12345") end end it "creates the current_resource object and sets its revision to the current deployment's revision as long as we're not exporting" do allow(@provider).to receive(:find_current_revision).and_return("11410") @provider.new_resource.instance_variable_set :@action, [:checkout] @provider.load_current_resource expect(@provider.current_resource.name).to eql(@resource.name) expect(@provider.current_resource.revision).to eql("11410") end context "resolving revisions to an integer" do before do @stdout = double("stdout") @stderr = double("stderr") @resource.svn_info_args "--no-auth-cache" end it "returns the revision number as is if it's already an integer" do expect(@provider.revision_int).to eql("12345") end it "queries the server and resolves the revision if it's not an integer (i.e. 'HEAD')" do example_svn_info = "Path: .\n" + "URL: http://svn.example.org/trunk/myapp\n" + "Repository Root: http://svn.example.org\n" + "Repository UUID: d62ff500-7bbc-012c-85f1-0026b0e37c24\n" + "Revision: 11739\nNode Kind: directory\n" + "Schedule: normal\n" + "Last Changed Author: codeninja\n" + "Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision "Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n" exitstatus = double("exitstatus") allow(exitstatus).to receive(:exitstatus).and_return(0) @resource.revision "HEAD" allow(@stdout).to receive(:string).and_return(example_svn_info) allow(@stderr).to receive(:string).and_return("") expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", {:cwd=>Dir.tmpdir}] expect(@provider).to receive(:popen4).with(*expected_command). and_yield("no-pid","no-stdin",@stdout,@stderr). and_return(exitstatus) expect(@provider.revision_int).to eql("11410") end it "returns a helpful message if data from `svn info` can't be parsed" do example_svn_info = "some random text from an error message\n" exitstatus = double("exitstatus") allow(exitstatus).to receive(:exitstatus).and_return(0) @resource.revision "HEAD" allow(@stdout).to receive(:string).and_return(example_svn_info) allow(@stderr).to receive(:string).and_return("") expect(@provider).to receive(:popen4).and_yield("no-pid","no-stdin",@stdout,@stderr). and_return(exitstatus) expect {@provider.revision_int}.to raise_error(RuntimeError, "Could not parse `svn info` data: some random text from an error message") end it "responds to :revision_slug as an alias for revision_sha" do expect(@provider).to respond_to(:revision_slug) end end it "generates a checkout command with default options" do expect(@provider.checkout_command).to eql("svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir") end it "generates a checkout command with authentication" do @resource.svn_username "deployNinja" @resource.svn_password "vanish!" expect(@provider.checkout_command).to eql("svn checkout -q --username deployNinja --password vanish! " + "-r12345 http://svn.example.org/trunk/ /my/deploy/dir") end it "generates a checkout command with arbitrary options" do @resource.svn_arguments "--no-auth-cache" expect(@provider.checkout_command).to eql("svn checkout --no-auth-cache -q -r12345 "+ "http://svn.example.org/trunk/ /my/deploy/dir") end it "generates a sync command with default options" do expect(@provider.sync_command).to eql("svn update -q -r12345 /my/deploy/dir") end it "generates an export command with default options" do expect(@provider.export_command).to eql("svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir") end it "doesn't try to find the current revision when loading the resource if running an export" do @provider.new_resource.instance_variable_set :@action, [:export] expect(@provider).not_to receive(:find_current_revision) @provider.load_current_resource end it "doesn't try to find the current revision when loading the resource if running a force export" do @provider.new_resource.instance_variable_set :@action, [:force_export] expect(@provider).not_to receive(:find_current_revision) @provider.load_current_resource end it "runs an export with the --force option" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expected_cmd = "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir" expect(@provider).to receive(:shell_out!).with(expected_cmd, {}) @provider.run_action(:force_export) expect(@resource).to be_updated end it "runs the checkout command for action_checkout" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expected_cmd = "svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir" expect(@provider).to receive(:shell_out!).with(expected_cmd, {}) @provider.run_action(:checkout) expect(@resource).to be_updated end it "raises an error if the svn checkout command would fail because the enclosing directory doesn't exist" do expect {@provider.run_action(:sync)}.to raise_error(Chef::Exceptions::MissingParentDirectory) end it "should not checkout if the destination exists or is a non empty directory" do allow(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(false) allow(::File).to receive(:exist?).with("/my/deploy/dir").and_return(true) allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) allow(::Dir).to receive(:entries).with("/my/deploy/dir").and_return(['.','..','foo','bar']) expect(@provider).not_to receive(:checkout_command) @provider.run_action(:checkout) expect(@resource).not_to be_updated end it "runs commands with the user and group specified in the resource" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) @resource.user "whois" @resource.group "thisis" expected_cmd = "svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir" expect(@provider).to receive(:shell_out!).with(expected_cmd, {user: "whois", group: "thisis"}) @provider.run_action(:checkout) expect(@resource).to be_updated end it "does a checkout for action_sync if there's no deploy dir" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").twice.and_return(false) expect(@provider).to receive(:action_checkout) @provider.run_action(:sync) end it "does a checkout for action_sync if the deploy dir exists but is empty" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").twice.and_return(false) expect(@provider).to receive(:action_checkout) @provider.run_action(:sync) end it "runs the sync_command on action_sync if the deploy dir exists and isn't empty" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(true) allow(@provider).to receive(:find_current_revision).and_return("11410") allow(@provider).to receive(:current_revision_matches_target_revision?).and_return(false) expected_cmd = "svn update -q -r12345 /my/deploy/dir" expect(@provider).to receive(:shell_out!).with(expected_cmd, {}) @provider.run_action(:sync) expect(@resource).to be_updated end it "does not fetch any updates if the remote revision matches the current revision" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(true) allow(@provider).to receive(:find_current_revision).and_return('12345') allow(@provider).to receive(:current_revision_matches_target_revision?).and_return(true) @provider.run_action(:sync) expect(@resource).not_to be_updated end it "runs the export_command on action_export" do allow(::File).to receive(:directory?).with("/my/deploy").and_return(true) expected_cmd = "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir" expect(@provider).to receive(:shell_out!).with(expected_cmd, {}) @provider.run_action(:export) expect(@resource).to be_updated end end chef-12.3.0/spec/unit/provider/link_spec.rb0000644000004100000410000002327012520074675020605 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: John Keiser () # 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 'ostruct' require 'spec_helper' if Chef::Platform.windows? require 'chef/win32/file' #probably need this in spec_helper end describe Chef::Resource::Link, :not_supported_on_win2k3 do let(:provider) do node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, @events) Chef::Provider::Link.new(new_resource, run_context) end let(:new_resource) do result = Chef::Resource::Link.new("#{CHEF_SPEC_DATA}/fofile-link") result.to "#{CHEF_SPEC_DATA}/fofile" result end def canonicalize(path) Chef::Platform.windows? ? path.gsub('/', '\\') : path end describe "when the target is a symlink" do before(:each) do lstat = double("stats", :ino => 5) allow(lstat).to receive(:uid).and_return(501) allow(lstat).to receive(:gid).and_return(501) allow(lstat).to receive(:mode).and_return(0777) allow(File).to receive(:lstat).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(lstat) allow(provider.file_class).to receive(:symlink?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(true) allow(provider.file_class).to receive(:readlink).with("#{CHEF_SPEC_DATA}/fofile-link").and_return("#{CHEF_SPEC_DATA}/fofile") end describe "to a file that exists" do before do allow(File).to receive(:exist?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(true) new_resource.owner 501 # only loaded in current_resource if present in new new_resource.group 501 provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should set the link type" do expect(provider.current_resource.link_type).to eq(:symbolic) end it "should update the source of the existing link with the links target" do expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile")) end it "should set the owner" do expect(provider.current_resource.owner).to eq(501) end it "should set the group" do expect(provider.current_resource.group).to eq(501) end # We test create in unit tests because there is no other way to ensure # it does no work. Other create and delete scenarios are covered in # the functional tests for links. context 'when the desired state is identical' do let(:new_resource) do result = Chef::Resource::Link.new("#{CHEF_SPEC_DATA}/fofile-link") result.to "#{CHEF_SPEC_DATA}/fofile" result end it 'create does no work' do expect(provider.access_controls).not_to receive(:set_all) provider.run_action(:create) end end end describe "to a file that doesn't exist" do before do allow(File).to receive(:exist?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(false) allow(provider.file_class).to receive(:symlink?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(true) allow(provider.file_class).to receive(:readlink).with("#{CHEF_SPEC_DATA}/fofile-link").and_return("#{CHEF_SPEC_DATA}/fofile") new_resource.owner "501" # only loaded in current_resource if present in new new_resource.group "501" provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should set the link type" do expect(provider.current_resource.link_type).to eq(:symbolic) end it "should update the source of the existing link to the link's target" do expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile")) end it "should not set the owner" do expect(provider.current_resource.owner).to be_nil end it "should not set the group" do expect(provider.current_resource.group).to be_nil end end end describe "when the target doesn't exist" do before do allow(File).to receive(:exists?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(false) allow(provider.file_class).to receive(:symlink?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(false) provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should update the source of the existing link to nil" do expect(provider.current_resource.to).to be_nil end it "should not set the owner" do expect(provider.current_resource.owner).to eq(nil) end it "should not set the group" do expect(provider.current_resource.group).to eq(nil) end end describe "when the target is a regular old file" do before do stat = double("stats", :ino => 5) allow(stat).to receive(:uid).and_return(501) allow(stat).to receive(:gid).and_return(501) allow(stat).to receive(:mode).and_return(0755) allow(provider.file_class).to receive(:stat).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(stat) allow(File).to receive(:exists?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(true) allow(provider.file_class).to receive(:symlink?).with("#{CHEF_SPEC_DATA}/fofile-link").and_return(false) end describe "and the source does not exist" do before do allow(File).to receive(:exists?).with("#{CHEF_SPEC_DATA}/fofile").and_return(false) provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should update the current source of the existing link with an empty string" do expect(provider.current_resource.to).to eq('') end it "should not set the owner" do expect(provider.current_resource.owner).to eq(nil) end it "should not set the group" do expect(provider.current_resource.group).to eq(nil) end end describe "and the source exists" do before do stat = double("stats", :ino => 6) allow(stat).to receive(:uid).and_return(502) allow(stat).to receive(:gid).and_return(502) allow(stat).to receive(:mode).and_return(0644) allow(provider.file_class).to receive(:stat).with("#{CHEF_SPEC_DATA}/fofile").and_return(stat) allow(File).to receive(:exists?).with("#{CHEF_SPEC_DATA}/fofile").and_return(true) provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should update the current source of the existing link with an empty string" do expect(provider.current_resource.to).to eq('') end it "should not set the owner" do expect(provider.current_resource.owner).to eq(nil) end it "should not set the group" do expect(provider.current_resource.group).to eq(nil) end end describe "and is hardlinked to the source" do before do stat = double("stats", :ino => 5) allow(stat).to receive(:uid).and_return(502) allow(stat).to receive(:gid).and_return(502) allow(stat).to receive(:mode).and_return(0644) allow(provider.file_class).to receive(:stat).with("#{CHEF_SPEC_DATA}/fofile").and_return(stat) allow(File).to receive(:exists?).with("#{CHEF_SPEC_DATA}/fofile").and_return(true) provider.load_current_resource end it "should set the symlink target" do expect(provider.current_resource.target_file).to eq("#{CHEF_SPEC_DATA}/fofile-link") end it "should set the link type" do expect(provider.current_resource.link_type).to eq(:hard) end it "should update the source of the existing link to the link's target" do expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile")) end it "should not set the owner" do expect(provider.current_resource.owner).to eq(nil) end it "should not set the group" do expect(provider.current_resource.group).to eq(nil) end # We test create in unit tests because there is no other way to ensure # it does no work. Other create and delete scenarios are covered in # the functional tests for links. context 'when the desired state is identical' do let(:new_resource) do result = Chef::Resource::Link.new("#{CHEF_SPEC_DATA}/fofile-link") result.to "#{CHEF_SPEC_DATA}/fofile" result.link_type :hard result end it 'create does no work' do expect(provider.file_class).not_to receive(:symlink) expect(provider.file_class).not_to receive(:link) expect(provider.access_controls).not_to receive(:set_all) provider.run_action(:create) end end end end end chef-12.3.0/spec/unit/provider/group/0000755000004100000410000000000012520074675017441 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/group/windows_spec.rb0000644000004100000410000000650512520074675022500 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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' class Chef class Util class Windows class NetGroup end end end end describe Chef::Provider::Group::Windows do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("staff") @net_group = double("Chef::Util::Windows::NetGroup") allow(Chef::Util::Windows::NetGroup).to receive(:new).and_return(@net_group) @provider = Chef::Provider::Group::Windows.new(@new_resource, @run_context) end describe "when creating the group" do it "should call @net_group.local_add" do expect(@net_group).to receive(:local_set_members).with([]) expect(@net_group).to receive(:local_add) @provider.create_group end end describe "manage_group" do before do @new_resource.members([ "us" ]) @current_resource = Chef::Resource::Group.new("staff") @current_resource.members [ "all", "your", "base" ] allow(Chef::Util::Windows::NetGroup).to receive(:new).and_return(@net_group) allow(@net_group).to receive(:local_add_members) allow(@net_group).to receive(:local_set_members) allow(@provider).to receive(:local_group_name_to_sid) @provider.current_resource = @current_resource end it "should call @net_group.local_set_members" do allow(@new_resource).to receive(:append).and_return(false) expect(@net_group).to receive(:local_set_members).with(@new_resource.members) @provider.manage_group end it "should call @net_group.local_add_members" do allow(@new_resource).to receive(:append).and_return(true) expect(@net_group).to receive(:local_add_members).with(@new_resource.members) @provider.manage_group end end describe "remove_group" do before do allow(Chef::Util::Windows::NetGroup).to receive(:new).and_return(@net_group) allow(@provider).to receive(:run_command).and_return(true) end it "should call @net_group.local_delete" do expect(@net_group).to receive(:local_delete) @provider.remove_group end end end describe Chef::Provider::Group::Windows, "NetGroup" do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("Creating a new group") @new_resource.group_name "Remote Desktop Users" end it 'sets group_name correctly' do expect(Chef::Util::Windows::NetGroup).to receive(:new).with("Remote Desktop Users") Chef::Provider::Group::Windows.new(@new_resource, @run_context) end end chef-12.3.0/spec/unit/provider/group/usermod_spec.rb0000644000004100000410000001076712520074675022471 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Group::Usermod do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("wheel") @new_resource.members [ "all", "your", "base" ] @new_resource.excluded_members [ ] @provider = Chef::Provider::Group::Usermod.new(@new_resource, @run_context) allow(@provider).to receive(:run_command) end describe "modify_group_members" do describe "with an empty members array" do before do allow(@new_resource).to receive(:append).and_return(true) allow(@new_resource).to receive(:members).and_return([]) end it "should log an appropriate message" do expect(@provider).not_to receive(:shell_out!) @provider.modify_group_members end end describe "with supplied members" do platforms = { "openbsd" => "-G", "netbsd" => "-G", "solaris" => "-a -G", "suse" => "-a -G", "opensuse" => "-a -G", "smartos" => "-G", "omnios" => "-G" } before do allow(@new_resource).to receive(:members).and_return(["all", "your", "base"]) allow(File).to receive(:exists?).and_return(true) end it "should raise an error when setting the entire group directly" do @provider.define_resource_requirements @provider.load_current_resource @provider.instance_variable_set("@group_exists", true) @provider.action = :modify expect { @provider.run_action(@provider.process_resource_requirements) }.to raise_error(Chef::Exceptions::Group, "setting group members directly is not supported by #{@provider.to_s}, must set append true in group") end it "should raise an error when excluded_members are set" do @provider.define_resource_requirements @provider.load_current_resource @provider.instance_variable_set("@group_exists", true) @provider.action = :modify allow(@new_resource).to receive(:append).and_return(true) allow(@new_resource).to receive(:excluded_members).and_return(["someone"]) expect { @provider.run_action(@provider.process_resource_requirements) }.to raise_error(Chef::Exceptions::Group, "excluded_members is not supported by #{@provider.to_s}") end platforms.each do |platform, flags| it "should usermod each user when the append option is set on #{platform}" do current_resource = @new_resource.dup current_resource.members([ ]) @provider.current_resource = current_resource @node.automatic_attrs[:platform] = platform allow(@new_resource).to receive(:append).and_return(true) expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel all") expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel your") expect(@provider).to receive(:shell_out!).with("usermod #{flags} wheel base") @provider.modify_group_members end end end end describe "when loading the current resource" do before(:each) do allow(File).to receive(:exists?).and_return(false) @provider.action = :create @provider.define_resource_requirements end it "should raise an error if the required binary /usr/sbin/usermod doesn't exist" do allow(File).to receive(:exists?).and_return(true) expect(File).to receive(:exists?).with("/usr/sbin/usermod").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "shouldn't raise an error if the required binaries exist" do allow(File).to receive(:exists?).and_return(true) expect { @provider.process_resource_requirements }.not_to raise_error end end end chef-12.3.0/spec/unit/provider/group/gpasswd_spec.rb0000644000004100000410000001040112520074675022444 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Group::Gpasswd, "modify_group_members" do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("wheel") @new_resource.members %w{lobster rage fist} @new_resource.append false @provider = Chef::Provider::Group::Gpasswd.new(@new_resource, @run_context) #@provider.stub(:run_command).and_return(true) end describe "when determining the current group state" do before (:each) do @provider.action = :create @provider.load_current_resource @provider.define_resource_requirements end # Checking for required binaries is already done in the spec # for Chef::Provider::Group - no need to repeat it here. We'll # include only what's specific to this provider. it "should raise an error if the required binary /usr/bin/gpasswd doesn't exist" do allow(File).to receive(:exists?).and_return(true) expect(File).to receive(:exists?).with("/usr/bin/gpasswd").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "shouldn't raise an error if the required binaries exist" do allow(File).to receive(:exists?).and_return(true) expect { @provider.process_resource_requirements }.not_to raise_error end end describe "after the group's current state is known" do before do @current_resource = @new_resource.dup @provider.current_resource = @new_resource end describe "when no group members are specified and append is not set" do before do @new_resource.append(false) @new_resource.members([]) end it "logs a message and sets group's members to 'none'" do expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: none") expect(@provider).to receive(:shell_out!).with("gpasswd -M \"\" wheel") @provider.modify_group_members end end describe "when no group members are specified and append is set" do before do @new_resource.append(true) @new_resource.members([]) end it "does not modify group membership" do expect(@provider).not_to receive(:shell_out!) @provider.modify_group_members end end describe "when the resource specifies group members" do it "should log an appropriate debug message" do expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: lobster, rage, fist") allow(@provider).to receive(:shell_out!) @provider.modify_group_members end it "should run gpasswd with the members joined by ',' followed by the target group" do expect(@provider).to receive(:shell_out!).with("gpasswd -M lobster,rage,fist wheel") @provider.modify_group_members end describe "when no user exists in the system" do before do current_resource = @new_resource.dup current_resource.members([ ]) @provider.current_resource = current_resource end it "should run gpasswd individually for each user when the append option is set" do @new_resource.append(true) expect(@provider).to receive(:shell_out!).with("gpasswd -a lobster wheel") expect(@provider).to receive(:shell_out!).with("gpasswd -a rage wheel") expect(@provider).to receive(:shell_out!).with("gpasswd -a fist wheel") @provider.modify_group_members end end end end end chef-12.3.0/spec/unit/provider/group/groupadd_spec.rb0000644000004100000410000001515612520074675022615 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Group::Groupadd, "set_options" do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("aj") @new_resource.gid(50) @new_resource.members(["root", "aj"]) @new_resource.system false @new_resource.non_unique false @current_resource = Chef::Resource::Group.new("aj") @current_resource.gid(50) @current_resource.members(["root", "aj"]) @current_resource.system false @current_resource.non_unique false @provider = Chef::Provider::Group::Groupadd.new(@new_resource, @run_context) @provider.current_resource = @current_resource end field_list = { :gid => "-g" } field_list.each do |attribute, option| it "should check for differences in #{attribute.to_s} between the current and new resources" do expect(@new_resource).to receive(attribute) expect(@current_resource).to receive(attribute) @provider.set_options end it "should set the option for #{attribute} if the new resources #{attribute} is not null" do allow(@new_resource).to receive(attribute).and_return("wowaweea") expect(@provider.set_options).to eql(" #{option} '#{@new_resource.send(attribute)}' #{@new_resource.group_name}") end end it "should combine all the possible options" do match_string = "" field_list.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option| allow(@new_resource).to receive(attribute).and_return("hola") match_string << " #{option} 'hola'" end match_string << " aj" expect(@provider.set_options).to eql(match_string) end describe "when we want to create a system group" do it "should not set groupadd_options '-r' when system is false" do @new_resource.system(false) expect(@provider.groupadd_options).not_to match(/-r/) end it "should set groupadd -r if system is true" do @new_resource.system(true) expect(@provider.groupadd_options).to eq(" -r") end end describe "when we want to create a non_unique gid group" do it "should not set groupadd_options '-o' when non_unique is false" do @new_resource.non_unique(false) expect(@provider.groupadd_options).not_to match(/-o/) end it "should set groupadd -o if non_unique is true" do @new_resource.non_unique(true) expect(@provider.groupadd_options).to eq(" -o") end end end describe Chef::Provider::Group::Groupadd, "create_group" do before do @node = Chef::Node.new @new_resource = Chef::Resource::Group.new("aj") @provider = Chef::Provider::Group::Groupadd.new(@node, @new_resource) allow(@provider).to receive(:run_command).and_return(true) allow(@provider).to receive(:set_options).and_return(" monkey") allow(@provider).to receive(:groupadd_options).and_return("") allow(@provider).to receive(:modify_group_members).and_return(true) end it "should run groupadd with the return of set_options" do expect(@provider).to receive(:run_command).with({ :command => "groupadd monkey" }).and_return(true) @provider.create_group end it "should modify the group members" do expect(@provider).to receive(:modify_group_members).and_return(true) @provider.create_group end end describe Chef::Provider::Group::Groupadd do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("aj") @provider = Chef::Provider::Group::Groupadd.new(@new_resource, @run_context) allow(@provider).to receive(:run_command).and_return(true) allow(@provider).to receive(:set_options).and_return(" monkey") end describe "manage group" do it "should run groupmod with the return of set_options" do allow(@provider).to receive(:modify_group_members).and_return(true) expect(@provider).to receive(:run_command).with({ :command => "groupmod monkey" }).and_return(true) @provider.manage_group end it "should modify the group members" do expect(@provider).to receive(:modify_group_members).and_return(true) @provider.manage_group end end describe "remove_group" do it "should run groupdel with the new resources group name" do expect(@provider).to receive(:run_command).with({ :command => "groupdel aj" }).and_return(true) @provider.remove_group end end [:add_member, :remove_member, :set_members].each do |m| it "should raise an error when calling #{m}" do expect { @provider.send(m, [ ]) }.to raise_error(Chef::Exceptions::Group, "you must override #{m} in #{@provider.to_s}") end end describe "load_current_resource" do before do allow(File).to receive(:exists?).and_return(false) @provider.define_resource_requirements end it "should raise an error if the required binary /usr/sbin/groupadd doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "should raise an error if the required binary /usr/sbin/groupmod doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(true) expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "should raise an error if the required binary /usr/sbin/groupdel doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/groupadd").and_return(true) expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(true) expect(File).to receive(:exists?).with("/usr/sbin/groupdel").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end end end chef-12.3.0/spec/unit/provider/group/dscl_spec.rb0000644000004100000410000002764712520074675021745 0ustar www-datawww-data# # Author:: Dreamcat4 () # 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 'spec_helper' describe Chef::Provider::Group::Dscl do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("aj") @current_resource = Chef::Resource::Group.new("aj") @provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context) @provider.current_resource = @current_resource @status = double(:stdout => "\n", :stderr => "", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(@status) end it "should run shell_out with the supplied array of arguments appended to the dscl command" do expect(@provider).to receive(:shell_out).with("dscl . -cmd /Path arg1 arg2") @provider.dscl("cmd", "/Path", "arg1", "arg2") end it "should return an array of four elements - cmd, status, stdout, stderr" do dscl_retval = @provider.dscl("cmd /Path args") expect(dscl_retval).to be_a_kind_of(Array) expect(dscl_retval).to eq(["dscl . -cmd /Path args",@status,"\n",""]) end describe "safe_dscl" do before do @node = Chef::Node.new @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource) allow(@provider).to receive(:dscl).and_return(["cmd", @status, "stdout", "stderr"]) end it "should run dscl with the supplied cmd /Path args" do expect(@provider).to receive(:dscl).with("cmd /Path args") @provider.safe_dscl("cmd /Path args") end describe "with the dscl command returning a non zero exit status for a delete" do before do @status = double("Process::Status", :exitstatus => 1) allow(@provider).to receive(:dscl).and_return(["cmd", @status, "stdout", "stderr"]) end it "should return an empty string of standard output for a delete" do safe_dscl_retval = @provider.safe_dscl("delete /Path args") expect(safe_dscl_retval).to be_a_kind_of(String) expect(safe_dscl_retval).to eq("") end it "should raise an exception for any other command" do expect { @provider.safe_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::Group) end end describe "with the dscl command returning no such key" do before do allow(@provider).to receive(:dscl).and_return(["cmd", @status, "No such key: ", "stderr"]) end it "should raise an exception" do expect { @provider.safe_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::Group) end end describe "with the dscl command returning a zero exit status" do it "should return the third array element, the string of standard output" do safe_dscl_retval = @provider.safe_dscl("cmd /Path args") expect(safe_dscl_retval).to be_a_kind_of(String) expect(safe_dscl_retval).to eq("stdout") end end end describe "get_free_gid" do before do @node = Chef::Node.new @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource) allow(@provider).to receive(:safe_dscl).and_return("\naj 200\njt 201\n") end it "should run safe_dscl with list /Groups gid" do expect(@provider).to receive(:safe_dscl).with("list /Groups gid") @provider.get_free_gid end it "should return the first unused gid number on or above 200" do expect(@provider.get_free_gid).to equal(202) end it "should raise an exception when the search limit is exhausted" do search_limit = 1 expect { @provider.get_free_gid(search_limit) }.to raise_error(RuntimeError) end end describe "gid_used?" do before do @node = Chef::Node.new @provider = Chef::Provider::Group::Dscl.new(@node, @new_resource) allow(@provider).to receive(:safe_dscl).and_return("\naj 500\n") end it "should run safe_dscl with list /Groups gid" do expect(@provider).to receive(:safe_dscl).with("list /Groups gid") @provider.gid_used?(500) end it "should return true for a used gid number" do expect(@provider.gid_used?(500)).to be_truthy end it "should return false for an unused gid number" do expect(@provider.gid_used?(501)).to be_falsey end it "should return false if not given any valid gid number" do expect(@provider.gid_used?(nil)).to be_falsey end end describe "set_gid" do describe "with the new resource and a gid number which is already in use" do before do allow(@provider).to receive(:gid_used?).and_return(true) end it "should raise an exception if the new resources gid is already in use" do expect { @provider.set_gid }.to raise_error(Chef::Exceptions::Group) end end describe "with no gid number for the new resources" do it "should run get_free_gid and return a valid, unused gid number" do expect(@provider).to receive(:get_free_gid).and_return(501) @provider.set_gid end end describe "with blank gid number for the new resources" do before do @new_resource.instance_variable_set(:@gid, nil) allow(@new_resource).to receive(:safe_dscl) end it "should run get_free_gid and return a valid, unused gid number" do expect(@provider).to receive(:get_free_gid).and_return(501) @provider.set_gid end end describe "with a valid gid number which is not already in use" do it "should run safe_dscl with create /Groups/group PrimaryGroupID gid" do allow(@provider).to receive(:get_free_gid).and_return(50) expect(@provider).to receive(:safe_dscl).with("list /Groups gid") expect(@provider).to receive(:safe_dscl).with("create /Groups/aj PrimaryGroupID 50").and_return(true) @provider.set_gid end end end describe "set_members" do describe "with existing members in the current resource and append set to false in the new resource" do before do allow(@new_resource).to receive(:members).and_return([]) allow(@new_resource).to receive(:append).and_return(false) allow(@current_resource).to receive(:members).and_return(["all", "your", "base"]) end it "should log an appropriate message" do expect(Chef::Log).to receive(:debug).with("group[aj] removing group members all your base") @provider.set_members end it "should run safe_dscl with create /Groups/group GroupMembership to clear the Group's UID list" do expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembers ''").and_return(true) expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembership ''").and_return(true) @provider.set_members end end describe "with supplied members in the new resource" do before do @new_resource.members(["all", "your", "base"]) @current_resource.members([]) end it "should log an appropriate debug message" do expect(Chef::Log).to receive(:debug).with("group[aj] setting group members all, your, base") @provider.set_members end it "should run safe_dscl with append /Groups/group GroupMembership and group members all, your, base" do expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembers ''").and_return(true) expect(@provider).to receive(:safe_dscl).with("append /Groups/aj GroupMembership all your base").and_return(true) expect(@provider).to receive(:safe_dscl).with("create /Groups/aj GroupMembership ''").and_return(true) @provider.set_members end end describe "with no members in the new resource" do before do @new_resource.append(true) @new_resource.members([]) end it "should not call safe_dscl" do expect(@provider).not_to receive(:safe_dscl) @provider.set_members end end end describe "when loading the current system state" do before (:each) do @provider.action = :create @provider.load_current_resource @provider.define_resource_requirements end it "raises an error if the required binary /usr/bin/dscl doesn't exist" do expect(File).to receive(:exists?).with("/usr/bin/dscl").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "doesn't raise an error if /usr/bin/dscl exists" do allow(File).to receive(:exists?).and_return(true) expect { @provider.process_resource_requirements }.not_to raise_error end end describe "when creating the group" do it "creates the group, password field, gid, and sets group membership" do expect(@provider).to receive(:set_gid).and_return(true) expect(@provider).to receive(:set_members).and_return(true) expect(@provider).to receive(:safe_dscl).with("create /Groups/aj Password '*'") expect(@provider).to receive(:safe_dscl).with("create /Groups/aj") @provider.create_group end end describe "managing the group" do it "should manage the group_name if it changed and the new resources group_name is not null" do @current_resource.group_name("oldval") @new_resource.group_name("newname") expect(@provider).to receive(:set_members).and_return(true) expect(@provider).to receive(:safe_dscl).with("create /Groups/newname") expect(@provider).to receive(:safe_dscl).with("create /Groups/newname Password '*'") @provider.manage_group end it "should manage the gid if it changed and the new resources gid is not null" do @current_resource.gid(23) @new_resource.gid(42) expect(@provider).to receive(:set_gid) @provider.manage_group end it "should manage the members if it changed and the new resources members is not null" do @current_resource.members(%{charlie root}) @new_resource.members(%{crab revenge}) expect(@provider).to receive(:set_members) @provider.manage_group end end describe "remove_group" do it "should run safe_dscl with delete /Groups/group and with the new resources group name" do expect(@provider).to receive(:safe_dscl).with("delete /Groups/aj").and_return(true) @provider.remove_group end end end describe 'Test DSCL loading' do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("group name aj") @new_resource.group_name("aj") @provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context) @output = <<-EOF AppleMetaNodeLocation: /Local/Default Comment: Test Group GeneratedUID: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA NestedGroups: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAB Password: * PrimaryGroupID: 999 RealName: TestGroup RecordName: com.apple.aj RecordType: dsRecTypeStandard:Groups GroupMembership: waka bar EOF allow(@provider).to receive(:safe_dscl).with("read /Groups/aj").and_return(@output) @current_resource = @provider.load_current_resource end it 'should parse gid properly' do allow(File).to receive(:exists?).and_return(true) expect(@current_resource.gid).to eq("999") end it 'should parse members properly' do allow(File).to receive(:exists?).and_return(true) expect(@current_resource.members).to eq(['waka', 'bar']) end end chef-12.3.0/spec/unit/provider/group/groupmod_spec.rb0000644000004100000410000001213112520074675022632 0ustar www-datawww-data# # Author:: Dan Crosta () # 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::Provider::Group::Groupmod do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("wheel") @new_resource.gid 123 @new_resource.members %w{lobster rage fist} @new_resource.append false @provider = Chef::Provider::Group::Groupmod.new(@new_resource, @run_context) end describe "manage_group" do describe "when determining the current group state" do it "should raise an error if the required binary /usr/sbin/group doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/group").and_return(false) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Group) end it "should raise an error if the required binary /usr/sbin/user doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/group").and_return(true) expect(File).to receive(:exists?).with("/usr/sbin/user").and_return(false) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Group) end it "shouldn't raise an error if the required binaries exist" do allow(File).to receive(:exists?).and_return(true) expect { @provider.load_current_resource }.not_to raise_error end end describe "after the group's current state is known" do before do @current_resource = @new_resource.dup @provider.current_resource = @current_resource end describe "when no group members are specified and append is not set" do before do @new_resource.append(false) @new_resource.members([]) end it "logs a message and sets group's members to 'none', then removes existing group members" do expect(Chef::Log).to receive(:debug).with("group[wheel] setting group members to: none") expect(@provider).to receive(:shell_out!).with("group mod -n wheel_bak wheel") expect(@provider).to receive(:shell_out!).with("group add -g '123' -o wheel") expect(@provider).to receive(:shell_out!).with("group del wheel_bak") @provider.manage_group end end describe "when no group members are specified and append is set" do before do @new_resource.append(true) @new_resource.members([]) end it "logs a message and does not modify group membership" do expect(Chef::Log).to receive(:debug).with("group[wheel] not changing group members, the group has no members to add") expect(@provider).not_to receive(:shell_out!) @provider.manage_group end end describe "when removing some group members" do before do @new_resource.append(false) @new_resource.members(%w{ lobster }) end it "updates group membership correctly" do allow(Chef::Log).to receive(:debug) expect(@provider).to receive(:shell_out!).with("group mod -n wheel_bak wheel") expect(@provider).to receive(:shell_out!).with("user mod -G wheel lobster") expect(@provider).to receive(:shell_out!).with("group add -g '123' -o wheel") expect(@provider).to receive(:shell_out!).with("group del wheel_bak") @provider.manage_group end end end end describe "create_group" do describe "when creating a new group" do before do @current_resource = Chef::Resource::Group.new("wheel") @provider.current_resource = @current_resource end it "should run a group add command and some user mod commands" do expect(@provider).to receive(:shell_out!).with("group add -g '123' wheel") expect(@provider).to receive(:shell_out!).with("user mod -G wheel lobster") expect(@provider).to receive(:shell_out!).with("user mod -G wheel rage") expect(@provider).to receive(:shell_out!).with("user mod -G wheel fist") @provider.create_group end end end describe "remove_group" do describe "when removing an existing group" do before do @current_resource = @new_resource.dup @provider.current_resource = @current_resource end it "should run a group del command" do expect(@provider).to receive(:shell_out!).with("group del wheel") @provider.remove_group end end end end chef-12.3.0/spec/unit/provider/group/pw_spec.rb0000644000004100000410000001174412520074675021435 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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 'spec_helper' describe Chef::Provider::Group::Pw do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Group.new("wheel") @new_resource.gid 50 @new_resource.members [ "root", "aj"] @current_resource = Chef::Resource::Group.new("aj") @current_resource.gid 50 @current_resource.members [ "root", "aj"] @provider = Chef::Provider::Group::Pw.new(@new_resource, @run_context) @provider.current_resource = @current_resource end describe "when setting options for the pw command" do it "does not set the gid option if gids match or are unmanaged" do expect(@provider.set_options).to eq(" wheel") end it "sets the option for gid if it is not nil" do @new_resource.gid(42) expect(@provider.set_options).to eql(" wheel -g '42'") end end describe "when creating a group" do it "should run pw groupadd with the return of set_options and set_members_option" do @new_resource.gid(23) expect(@provider).to receive(:run_command).with({ :command => "pw groupadd wheel -g '23' -M root,aj" }).and_return(true) @provider.create_group end end describe "when managing the group" do it "should run pw groupmod with the return of set_options" do @new_resource.gid(42) @new_resource.members(["someone"]) expect(@provider).to receive(:run_command).with({ :command => "pw groupmod wheel -g '42' -m someone" }).and_return(true) expect(@provider).to receive(:run_command).with({ :command => "pw groupmod wheel -g '42' -d root,aj" }).and_return(true) @provider.manage_group end end describe "when removing the group" do it "should run pw groupdel with the new resources group name" do expect(@provider).to receive(:run_command).with({ :command => "pw groupdel wheel" }).and_return(true) @provider.remove_group end end describe "when setting group membership" do describe "with an empty members array in both the new and current resource" do before do allow(@new_resource).to receive(:members).and_return([]) allow(@current_resource).to receive(:members).and_return([]) end it "should set no options" do expect(@provider.set_members_options).to eql([ ]) end end describe "with an empty members array in the new resource and existing members in the current resource" do before do allow(@new_resource).to receive(:members).and_return([]) allow(@current_resource).to receive(:members).and_return(["all", "your", "base"]) end it "should log an appropriate message" do expect(Chef::Log).to receive(:debug).with("group[wheel] removing group members: all,your,base") @provider.set_members_options end it "should set the -d option with the members joined by ','" do expect(@provider.set_members_options).to eql([ " -d all,your,base" ]) end end describe "with supplied members array in the new resource and an empty members array in the current resource" do before do allow(@new_resource).to receive(:members).and_return(["all", "your", "base"]) allow(@current_resource).to receive(:members).and_return([]) end it "should log an appropriate debug message" do expect(Chef::Log).to receive(:debug).with("group[wheel] adding group members: all,your,base") @provider.set_members_options end it "should set the -m option with the members joined by ','" do expect(@provider.set_members_options).to eql([ " -m all,your,base" ]) end end end describe"load_current_resource" do before (:each) do @provider.action = :create @provider.load_current_resource @provider.define_resource_requirements end it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/pw").and_return(false) expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) end it "shouldn't raise an error if /usr/sbin/pw exists" do allow(File).to receive(:exists?).and_return(true) expect { @provider.process_resource_requirements }.not_to raise_error end end end chef-12.3.0/spec/unit/provider/ifconfig/0000755000004100000410000000000012520074675020071 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/ifconfig/aix_spec.rb0000644000004100000410000001573512520074675022224 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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/exceptions' describe Chef::Provider::Ifconfig::Aix do before(:all) do @ifconfig_output = <<-IFCONFIG en1: flags=1e080863,480 inet 10.153.11.59 netmask 0xffff0000 broadcast 10.153.255.255 tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1 en0: flags=1e080863,480 metric 1 inet 172.29.174.58 netmask 0xffffc000 broadcast 172.29.191.255 tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1 lo0: flags=e08084b,c0 inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255 inet6 ::1%1/0 IFCONFIG end before(:each) do @node = Chef::Node.new @cookbook_collection = Chef::CookbookCollection.new([]) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) @provider = Chef::Provider::Ifconfig::Aix.new(@new_resource, @run_context) end describe "#load_current_resource" do before do @status = double(:stdout => @ifconfig_output, :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(@status) @new_resource.device "en0" end it "should load given interface with attributes." do current_resource = @provider.load_current_resource expect(current_resource.inet_addr).to eq("172.29.174.58") expect(current_resource.target).to eq(@new_resource.target) expect(current_resource.mask).to eq("255.255.192.0") expect(current_resource.bcast).to eq("172.29.191.255") expect(current_resource.metric).to eq("1") end end describe "#action_add" do it "should add an interface if it does not exist" do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) @provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)) end command = "chdev -l #{@new_resource.device} -a netaddr=#{@new_resource.name}" expect(@provider).to receive(:run_command).with(:command => command) @provider.run_action(:add) expect(@new_resource).to be_updated end it "should raise exception if metric attribute is set" do @new_resource.device "en0" @new_resource.metric "1" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) @provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)) end expect{@provider.run_action(:add)}.to raise_error(Chef::Exceptions::Ifconfig, "interface metric attribute cannot be set for :add action") end end describe "#action_enable" do it "should enable an interface if it does not exist" do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) @provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)) end command = "ifconfig #{@new_resource.device} #{@new_resource.name}" expect(@provider).to receive(:run_command).with(:command => command) @provider.run_action(:enable) expect(@new_resource).to be_updated end end describe "#action_disable" do it "should not disable an interface if it does not exist" do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) @provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)) end expect(@provider).not_to receive(:run_command) @provider.run_action(:disable) expect(@new_resource).not_to be_updated end context "interface exists" do before do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) current_resource.device @new_resource.device @provider.instance_variable_set("@current_resource", current_resource) end end it "should disable an interface if it exists" do command = "ifconfig #{@new_resource.device} down" expect(@provider).to receive(:run_command).with(:command => command) @provider.run_action(:disable) expect(@new_resource).to be_updated end end end describe "#action_delete" do it "should not delete an interface if it does not exist" do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) @provider.instance_variable_set("@current_resource", Chef::Resource::Ifconfig.new("10.0.0.1", @run_context)) end expect(@provider).not_to receive(:run_command) @provider.run_action(:delete) expect(@new_resource).not_to be_updated end context "interface exists" do before do @new_resource.device "en10" allow(@provider).to receive(:load_current_resource) do @provider.instance_variable_set("@status", double("Status", :exitstatus => 0)) current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) current_resource.device @new_resource.device @provider.instance_variable_set("@current_resource", current_resource) end end it "should delete an interface if it exists" do command = "chdev -l #{@new_resource.device} -a state=down" expect(@provider).to receive(:run_command).with(:command => command) @provider.run_action(:delete) expect(@new_resource).to be_updated end end end end chef-12.3.0/spec/unit/provider/ifconfig/debian_spec.rb0000644000004100000410000002715412520074675022663 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/exceptions' describe Chef::Provider::Ifconfig::Debian do let(:run_context) do node = Chef::Node.new cookbook_collection = Chef::CookbookCollection.new([]) events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, cookbook_collection, events) end let(:new_resource) do new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", run_context) new_resource.mask "255.255.254.0" new_resource.metric "1" new_resource.mtu "1500" new_resource.device "eth0" new_resource end let(:current_resource) { Chef::Resource::Ifconfig.new("10.0.0.1", run_context) } let(:provider) do status = double("Status", :exitstatus => 0) provider = Chef::Provider::Ifconfig::Debian.new(new_resource, run_context) provider.instance_variable_set("@status", status) provider.current_resource = current_resource allow(provider).to receive(:load_current_resource) allow(provider).to receive(:run_command) provider end let(:config_filename_ifaces) { "/etc/network/interfaces" } let(:config_filename_ifcfg) { "/etc/network/interfaces.d/ifcfg-#{new_resource.device}" } describe "generate_config" do context "when writing a file" do let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") } let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") } let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" } before do stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path) stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path) end it "should write a network-script" do provider.run_action(:add) expect(File.read(config_filename_ifcfg)).to match(/^iface eth0 inet static\s*$/) expect(File.read(config_filename_ifcfg)).to match(/^\s+address 10\.0\.0\.1\s*$/) expect(File.read(config_filename_ifcfg)).to match(/^\s+netmask 255\.255\.254\.0\s*$/) end context "when the interface_dot_d directory does not exist" do before do FileUtils.rmdir tempdir_path expect(File.exists?(tempdir_path)).to be_falsey end it "should create the /etc/network/interfaces.d directory" do provider.run_action(:add) expect(File.exists?(tempdir_path)).to be_truthy expect(File.directory?(tempdir_path)).to be_truthy end it "should mark the resource as updated" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end context "when the interface_dot_d directory exists" do before do expect(File.exists?(tempdir_path)).to be_truthy end it "should still mark the resource as updated (we still write a file to it)" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end end context "when the file is up-to-date" do let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") } let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") } let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" } before do stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path) stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path) config_file_ifcfg = StringIO.new(<<-EOF iface eth0 inet static address 10.0.0.1 netmask 255.255.254.0 EOF ) expect(File.exists?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist end context "when the /etc/network/interfaces file has the source line" do let(:expected_string) do <<-EOF a line source #{tempdir_path}/* another line EOF end before do tempfile.write(expected_string) tempfile.close expect(provider).not_to receive(:converge_by).with(/modifying #{tempfile.path} to source #{tempdir_path}/) end it "should preserve all the contents" do provider.run_action(:add) expect(IO.read(tempfile.path)).to eq(expected_string) end it "should not mark the resource as updated" do provider.run_action(:add) pending "superclass ifconfig provider is not idempotent" expect(new_resource.updated_by_last_action?).to be_falsey end end context "when the /etc/network/interfaces file does not have the source line" do let(:expected_string) do <<-EOF a line another line source #{tempdir_path}/* EOF end before do tempfile.write("a line\nanother line\n") tempfile.close allow(provider).to receive(:converge_by).and_call_original expect(provider).to receive(:converge_by).with(/modifying #{tempfile.path} to source #{tempdir_path}/).and_call_original end it "should preserve the original contents and add the source line" do provider.run_action(:add) expect(IO.read(tempfile.path)).to eq(expected_string) end it "should mark the resource as updated" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end end describe "when running under why run" do before do Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end context "when writing a file" do let(:config_file_ifcfg) { StringIO.new } let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") } let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") } let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" } before do stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path) stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path) expect(File).not_to receive(:new).with(config_filename_ifcfg, "w") end it "should write a network-script" do provider.run_action(:add) expect(config_file_ifcfg.string).not_to match(/^iface eth0 inet static\s*$/) expect(config_file_ifcfg.string).not_to match(/^\s+address 10\.0\.0\.1\s*$/) expect(config_file_ifcfg.string).not_to match(/^\s+netmask 255\.255\.254\.0\s*$/) end context "when the interface_dot_d directory does not exist" do before do FileUtils.rmdir tempdir_path expect(File.exists?(tempdir_path)).to be_falsey end it "should not create the /etc/network/interfaces.d directory" do provider.run_action(:add) expect(File.exists?(tempdir_path)).not_to be_truthy end it "should mark the resource as updated" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end context "when the interface_dot_d directory exists" do before do expect(File.exists?(tempdir_path)).to be_truthy end it "should still mark the resource as updated (we still write a file to it)" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end end context "when the file is up-to-date" do let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") } let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") } let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" } before do stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path) stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path) config_file_ifcfg = StringIO.new(<<-EOF iface eth0 inet static address 10.0.0.1 netmask 255.255.254.0 EOF ) expect(File).not_to receive(:new).with(config_filename_ifcfg, "w") expect(File.exists?(tempdir_path)).to be_truthy # since the file exists, the enclosing dir must also exist end context "when the /etc/network/interfaces file has the source line" do let(:expected_string) do <<-EOF a line source #{tempdir_path}/* another line EOF end before do tempfile.write(expected_string) tempfile.close end it "should preserve all the contents" do provider.run_action(:add) expect(IO.read(tempfile.path)).to eq(expected_string) end it "should not mark the resource as updated" do provider.run_action(:add) pending "superclass ifconfig provider is not idempotent" expect(new_resource.updated_by_last_action?).to be_falsey end end context "when the /etc/network/interfaces file does not have the source line" do let(:expected_string) do <<-EOF a line another line source #{tempdir_path}/* EOF end before do tempfile.write("a line\nanother line\n") tempfile.close end it "should preserve the original contents and not add the source line" do provider.run_action(:add) expect(IO.read(tempfile.path)).to eq("a line\nanother line\n") end it "should mark the resource as updated" do provider.run_action(:add) expect(new_resource.updated_by_last_action?).to be_truthy end end end end end describe "delete_config for action_delete" do let(:tempfile) { Tempfile.new("rspec-chef-ifconfig-debian") } let(:tempdir_path) { Dir.mktmpdir("rspec-chef-ifconfig-debian-dir") } let(:config_filename_ifcfg) { "#{tempdir_path}/ifcfg-#{new_resource.device}" } before do stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_FILE", tempfile.path) stub_const("Chef::Provider::Ifconfig::Debian::INTERFACES_DOT_D_DIR", tempdir_path) File.open(config_filename_ifcfg, "w") do |fh| fh.write "arbitrary text\n" fh.close end end after do Dir.rmdir(tempdir_path) end it "should delete network-script if it exists" do current_resource.device new_resource.device # belt and suspenders testing? expect_any_instance_of(Chef::Util::Backup).to receive(:do_backup).and_call_original # internal implementation detail of Ifconfig. expect_any_instance_of(Chef::Provider::File).to receive(:action_delete).and_call_original expect(File.exist?(config_filename_ifcfg)).to be_truthy provider.run_action(:delete) expect(File.exist?(config_filename_ifcfg)).to be_falsey end end end chef-12.3.0/spec/unit/provider/ifconfig/redhat_spec.rb0000644000004100000410000000553212520074675022704 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/exceptions' describe Chef::Provider::Ifconfig::Redhat do before do @node = Chef::Node.new @cookbook_collection = Chef::CookbookCollection.new([]) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) #This new_resource can be called anything --> it is not the same as in ifconfig.rb @new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) @new_resource.mask "255.255.254.0" @new_resource.metric "1" @new_resource.mtu "1500" @new_resource.device "eth0" @provider = Chef::Provider::Ifconfig::Redhat.new(@new_resource, @run_context) @current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) status = double("Status", :exitstatus => 0) @provider.instance_variable_set("@status", status) @provider.current_resource = @current_resource config_filename = "/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}" @config = double("chef-resource-file") expect(@provider).to receive(:resource_for_config).with(config_filename).and_return(@config) end describe "generate_config for action_add" do it "should write network-script for centos" do allow(@provider).to receive(:load_current_resource) allow(@provider).to receive(:run_command) expect(@config).to receive(:content) do |arg| expect(arg).to match(/^\s*DEVICE=eth0\s*$/) expect(arg).to match(/^\s*IPADDR=10\.0\.0\.1\s*$/) expect(arg).to match(/^\s*NETMASK=255\.255\.254\.0\s*$/) end expect(@config).to receive(:run_action).with(:create) expect(@config).to receive(:updated?).and_return(true) @provider.run_action(:add) end end describe "delete_config for action_delete" do it "should delete network-script if it exists for centos" do @current_resource.device @new_resource.device allow(@provider).to receive(:load_current_resource) allow(@provider).to receive(:run_command) expect(@config).to receive(:run_action).with(:delete) expect(@config).to receive(:updated?).and_return(true) @provider.run_action(:delete) end end end chef-12.3.0/spec/unit/provider/execute_spec.rb0000644000004100000410000001525712520074675021320 0ustar www-datawww-data# # Author:: Prajakta Purohit () # 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' describe Chef::Provider::Execute do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:provider) { Chef::Provider::Execute.new(new_resource, run_context) } let(:current_resource) { Chef::Resource::Ifconfig.new("foo_resource", run_context) } let(:opts) do { timeout: 3600, returns: 0, log_level: :info, log_tag: new_resource.to_s, live_stream: STDOUT, } end let(:new_resource) { Chef::Resource::Execute.new("foo_resource", run_context) } before do allow(Chef::Platform).to receive(:windows?) { false } @original_log_level = Chef::Log.level Chef::Log.level = :info allow(STDOUT).to receive(:tty?).and_return(true) end after do Chef::Log.level = @original_log_level end describe "#initialize" do it "should return a Chef::Provider::Execute provider" do expect(provider.class).to eql(Chef::Provider::Execute) end end describe "#load_current_resource" do before do expect(Chef::Resource::Execute).to receive(:new).with(new_resource.name).and_return(current_resource) end it "should return the current resource" do expect(provider.load_current_resource).to eql(current_resource) end it "our timeout should default to 3600" do provider.load_current_resource expect(provider.timeout).to eql(3600) end end describe "#action_run" do it "runs shell_out with the default options" do expect(provider).to receive(:shell_out!).with(new_resource.name, opts) expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end it "if you pass a command attribute, it runs the command" do new_resource.command "/usr/argelbargle/bin/oogachacka 12345" expect(provider).to receive(:shell_out!).with(new_resource.command, opts) expect(provider).to receive(:converge_by).with("execute #{new_resource.command}").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end it "should honor sensitive attribute" do new_resource.sensitive true # Since the resource is sensitive, it should not have :live_stream set opts.delete(:live_stream) expect(provider).to receive(:shell_out!).with(new_resource.name, opts) expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end it "should do nothing if the sentinel file exists" do new_resource.creates "/foo_resource" expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true) expect(provider).not_to receive(:shell_out!) expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).not_to be_updated end describe "when the user specifies a relative path without cwd" do before do expect(new_resource.cwd).to be_falsey new_resource.creates "foo_resource" end it "should warn in Chef-12", :chef_lt_13_only do expect(Chef::Log).to receive(:warn).with(/relative path/) expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true) expect(provider).not_to receive(:shell_out!) provider.run_action(:run) expect(new_resource).not_to be_updated end it "should raise if user specified relative path without cwd for Chef-13", :chef_gte_13_only do expect(Chef::Log).to receive(:warn).with(/relative path/) expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true) expect(provider).not_to receive(:shell_out!) expect { provider.run_action(:run) }.to raise_error # @todo: add a real error for Chef-13 end end it "should respect cwd options for 'creates'" do new_resource.cwd "/tmp" new_resource.creates "foo_resource" expect(FileTest).not_to receive(:exist?).with(new_resource.creates) expect(FileTest).to receive(:exist?).with(File.join("/tmp", new_resource.creates)).and_return(true) expect(Chef::Log).not_to receive(:warn) expect(provider).not_to receive(:shell_out!) provider.run_action(:run) expect(new_resource).not_to be_updated end it "should unset the live_stream if STDOUT is not a tty" do expect(STDOUT).to receive(:tty?).and_return(false) opts.delete(:live_stream) expect(provider).to receive(:shell_out!).with(new_resource.name, opts) expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end it "should unset the live_stream if chef is running as a daemon" do allow(Chef::Config).to receive(:[]).and_call_original expect(Chef::Config).to receive(:[]).with(:daemon).and_return(true) opts.delete(:live_stream) expect(provider).to receive(:shell_out!).with(new_resource.name, opts) expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end it "should unset the live_stream if we are not running with a log level of at least :info" do expect(Chef::Log).to receive(:info?).and_return(false) opts.delete(:live_stream) expect(provider).to receive(:shell_out!).with(new_resource.name, opts) expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original expect(Chef::Log).not_to receive(:warn) provider.run_action(:run) expect(new_resource).to be_updated end end end chef-12.3.0/spec/unit/provider/mount_spec.rb0000644000004100000410000001617212520074675021015 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Mount do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) do new_resource = Chef::Resource::Mount.new('/tmp/foo') new_resource.device "/dev/sdz1" new_resource.name "/tmp/foo" new_resource.mount_point "/tmp/foo" new_resource.fstype "ext3" new_resource end let(:current_resource) do # this abstract superclass has no load_current_resource to call current_resource = Chef::Resource::Mount.new('/tmp/foo') current_resource.device "/dev/sdz1" current_resource.name "/tmp/foo" current_resource.mount_point "/tmp/foo" current_resource.fstype "ext3" current_resource end let(:provider) do provider = Chef::Provider::Mount.new(new_resource, run_context) provider.current_resource = current_resource provider end describe "when the target state is a mounted filesystem" do it "should mount the filesystem if it isn't mounted" do allow(current_resource).to receive(:mounted).and_return(false) expect(provider).to receive(:mount_fs).and_return(true) provider.run_action(:mount) expect(new_resource).to be_updated_by_last_action end it "should not mount the filesystem if it is mounted" do allow(current_resource).to receive(:mounted).and_return(true) expect(provider).not_to receive(:mount_fs) provider.run_action(:mount) expect(new_resource).not_to be_updated_by_last_action end end describe "when the target state is an unmounted filesystem" do it "should umount the filesystem if it is mounted" do allow(current_resource).to receive(:mounted).and_return(true) expect(provider).to receive(:umount_fs).and_return(true) provider.run_action(:umount) expect(new_resource).to be_updated_by_last_action end it "should not umount the filesystem if it is not mounted" do allow(current_resource).to receive(:mounted).and_return(false) expect(provider).not_to receive(:umount_fs) provider.run_action(:umount) expect(new_resource).not_to be_updated_by_last_action end end describe "when the filesystem should be remounted and the resource supports remounting" do before do new_resource.supports[:remount] = true end it "should remount the filesystem if it is mounted" do allow(current_resource).to receive(:mounted).and_return(true) expect(provider).to receive(:remount_fs).and_return(true) provider.run_action(:remount) expect(new_resource).to be_updated_by_last_action end it "should not remount the filesystem if it is not mounted" do allow(current_resource).to receive(:mounted).and_return(false) expect(provider).not_to receive(:remount_fs) provider.run_action(:remount) expect(new_resource).not_to be_updated_by_last_action end end describe "when the filesystem should be remounted and the resource does not support remounting" do before do new_resource.supports[:remount] = false allow(current_resource).to receive(:mounted).and_return(true) end it "should try a umount/remount of the filesystem" do expect(provider).to receive(:umount_fs) expect(provider).to receive(:mounted?).and_return(true, false) expect(provider).to receive(:mount_fs) provider.run_action(:remount) expect(new_resource).to be_updated_by_last_action end it "should fail when it runs out of remounts" do provider.unmount_retries = 1 expect(provider).to receive(:umount_fs) expect(provider).to receive(:mounted?).and_return(true, true) expect{ provider.run_action(:remount) }.to raise_error(Chef::Exceptions::Mount) end end describe "when enabling the filesystem to be mounted" do it "should enable the mount if it isn't enable" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).not_to receive(:mount_options_unchanged?) expect(provider).to receive(:enable_fs).and_return(true) provider.run_action(:enable) expect(new_resource).to be_updated_by_last_action end it "should enable the mount if it is enabled and mount options have changed" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:mount_options_unchanged?).and_return(false) expect(provider).to receive(:enable_fs).and_return(true) provider.run_action(:enable) expect(new_resource).to be_updated_by_last_action end it "should not enable the mount if it is enabled and mount options have not changed" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:mount_options_unchanged?).and_return(true) expect(provider).not_to receive(:enable_fs) provider.run_action(:enable) expect(new_resource).not_to be_updated_by_last_action end end describe "when the target state is to disable the mount" do it "should disable the mount if it is enabled" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:disable_fs).and_return(true) provider.run_action(:disable) expect(new_resource).to be_updated_by_last_action end it "should not disable the mount if it isn't enabled" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).not_to receive(:disable_fs) provider.run_action(:disable) expect(new_resource).not_to be_updated_by_last_action end end it "should delegates the mount implementation to subclasses" do expect { provider.mount_fs }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should delegates the umount implementation to subclasses" do expect { provider.umount_fs }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should delegates the remount implementation to subclasses" do expect { provider.remount_fs }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should delegates the enable implementation to subclasses" do expect { provider.enable_fs }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should delegates the disable implementation to subclasses" do expect { provider.disable_fs }.to raise_error(Chef::Exceptions::UnsupportedAction) end end chef-12.3.0/spec/unit/provider/env_spec.rb0000644000004100000410000002556312520074675020447 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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::Env do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Env.new("FOO") @new_resource.value("bar") @provider = Chef::Provider::Env.new(@new_resource, @run_context) end it "assumes the key_name exists by default" do expect(@provider.key_exists).to be_truthy end describe "when loading the current status" do before do #@current_resource = @new_resource.clone #Chef::Resource::Env.stub(:new).and_return(@current_resource) @provider.current_resource = @current_resource allow(@provider).to receive(:env_value).with("FOO").and_return("bar") allow(@provider).to receive(:env_key_exists).and_return(true) end it "should create a current resource with the same name as the new resource" do @provider.load_current_resource expect(@provider.new_resource.name).to eq("FOO") end it "should set the key_name to the key name of the new resource" do @provider.load_current_resource expect(@provider.current_resource.key_name).to eq("FOO") end it "should check if the key_name exists" do expect(@provider).to receive(:env_key_exists).with("FOO").and_return(true) @provider.load_current_resource expect(@provider.key_exists).to be_truthy end it "should flip the value of exists if the key does not exist" do expect(@provider).to receive(:env_key_exists).with("FOO").and_return(false) @provider.load_current_resource expect(@provider.key_exists).to be_falsey end it "should return the current resource" do expect(@provider.load_current_resource).to be_a_kind_of(Chef::Resource::Env) end end describe "action_create" do before do @provider.key_exists = false allow(@provider).to receive(:create_env).and_return(true) allow(@provider).to receive(:modify_env).and_return(true) end it "should call create_env if the key does not exist" do expect(@provider).to receive(:create_env).and_return(true) @provider.action_create end it "should set the new_resources updated flag when it creates the key" do @provider.action_create expect(@new_resource).to be_updated end it "should check to see if the values are the same if the key exists" do @provider.key_exists = true expect(@provider).to receive(:requires_modify_or_create?).and_return(false) @provider.action_create end it "should call modify_env if the key exists and values are not equal" do @provider.key_exists = true allow(@provider).to receive(:requires_modify_or_create?).and_return(true) expect(@provider).to receive(:modify_env).and_return(true) @provider.action_create end it "should set the new_resources updated flag when it updates an existing value" do @provider.key_exists = true allow(@provider).to receive(:requires_modify_or_create?).and_return(true) allow(@provider).to receive(:modify_env).and_return(true) @provider.action_create expect(@new_resource).to be_updated end end describe "action_delete" do before(:each) do @provider.current_resource = @current_resource @provider.key_exists = false allow(@provider).to receive(:delete_element).and_return(false) allow(@provider).to receive(:delete_env).and_return(true) end it "should not call delete_env if the key does not exist" do expect(@provider).not_to receive(:delete_env) @provider.action_delete end it "should not call delete_element if the key does not exist" do expect(@provider).not_to receive(:delete_element) @provider.action_delete end it "should call delete_env if the key exists" do @provider.key_exists = true expect(@provider).to receive(:delete_env) @provider.action_delete end it "should set the new_resources updated flag to true if the key is deleted" do @provider.key_exists = true @provider.action_delete expect(@new_resource).to be_updated end end describe "action_modify" do before(:each) do @provider.current_resource = @current_resource @provider.key_exists = true allow(@provider).to receive(:modify_env).and_return(true) end it "should call modify_group if the key exists and values are not equal" do expect(@provider).to receive(:requires_modify_or_create?).and_return(true) expect(@provider).to receive(:modify_env).and_return(true) @provider.action_modify end it "should set the new resources updated flag to true if modify_env is called" do allow(@provider).to receive(:requires_modify_or_create?).and_return(true) allow(@provider).to receive(:modify_env).and_return(true) @provider.action_modify expect(@new_resource).to be_updated end it "should not call modify_env if the key exists but the values are equal" do expect(@provider).to receive(:requires_modify_or_create?).and_return(false) expect(@provider).not_to receive(:modify_env) @provider.action_modify end it "should raise a Chef::Exceptions::Env if the key doesn't exist" do @provider.key_exists = false expect { @provider.action_modify }.to raise_error(Chef::Exceptions::Env) end end describe "delete_element" do before(:each) do @current_resource = Chef::Resource::Env.new("FOO") @new_resource.delim ";" @new_resource.value "C:/bar/bin" @current_resource.value "C:/foo/bin;C:/bar/bin" @provider.current_resource = @current_resource end it "should return true if the element is not found" do allow(@new_resource).to receive(:value).and_return("C:/baz/bin") expect(@provider.delete_element).to eql(true) end it "should return false if the delim not defined" do allow(@new_resource).to receive(:delim).and_return(nil) expect(@provider.delete_element).to eql(false) end it "should return true if the element is deleted" do @new_resource.value("C:/foo/bin") expect(@provider).to receive(:create_env) expect(@provider.delete_element).to eql(true) expect(@new_resource).to be_updated end context "when new_resource's value contains the delimiter" do it "should return false if all the elements are deleted" do # This indicates that the entire key needs to be deleted @new_resource.value("C:/foo/bin;C:/bar/bin") expect(@provider.delete_element).to eql(false) expect(@new_resource).not_to be_updated # This will be updated in action_delete end it "should return true if any, but not all, of the elements are deleted" do @new_resource.value("C:/foo/bin;C:/notbaz/bin") expect(@provider).to receive(:create_env) expect(@provider.delete_element).to eql(true) expect(@new_resource).to be_updated end it "should return true if none of the elements are deleted" do @new_resource.value("C:/notfoo/bin;C:/notbaz/bin") expect(@provider.delete_element).to eql(true) expect(@new_resource).not_to be_updated end end end describe "requires_modify_or_create?" do before(:each) do @new_resource.value("C:/bar") @current_resource = @new_resource.clone @provider.current_resource = @current_resource end it "should return false if the values are equal" do expect(@provider.requires_modify_or_create?).to be_falsey end it "should return true if the values not are equal" do @new_resource.value("C:/elsewhere") expect(@provider.requires_modify_or_create?).to be_truthy end it "should return false if the current value contains the element" do @new_resource.delim(";") @current_resource.value("C:/bar;C:/foo;C:/baz") expect(@provider.requires_modify_or_create?).to be_falsey end it "should return true if the current value does not contain the element" do @new_resource.delim(";") @current_resource.value("C:/biz;C:/foo/bin;C:/baz") expect(@provider.requires_modify_or_create?).to be_truthy end context "when new_resource's value contains the delimiter" do it "should return false if all the current values are contained in specified order" do @new_resource.value("C:/biz;C:/baz") @new_resource.delim(";") @current_resource.value("C:/biz;C:/foo/bin;C:/baz") expect(@provider.requires_modify_or_create?).to be_falsey end it "should return true if any of the new values are not contained" do @new_resource.value("C:/biz;C:/baz;C:/bin") @new_resource.delim(";") @current_resource.value("C:/biz;C:/foo/bin;C:/baz") expect(@provider.requires_modify_or_create?).to be_truthy end it "should return true if values are contained in different order" do @new_resource.value("C:/biz;C:/baz") @new_resource.delim(";") @current_resource.value("C:/baz;C:/foo/bin;C:/biz") expect(@provider.requires_modify_or_create?).to be_truthy end end end describe "modify_env" do before(:each) do allow(@provider).to receive(:create_env).and_return(true) @new_resource.delim ";" @current_resource = Chef::Resource::Env.new("FOO") @current_resource.value "C:/foo/bin" @provider.current_resource = @current_resource end it "should not modify the variable passed to the resource" do new_value = "C:/bar/bin" passed_value = new_value.dup @new_resource.value(passed_value) @provider.modify_env expect(passed_value).to eq(new_value) end it "should only add values not already contained" do @new_resource.value("C:/foo;C:/bar;C:/baz") @current_resource.value("C:/bar;C:/baz;C:/foo/bar") @provider.modify_env expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar") end it "should reorder values to keep order which asked" do @new_resource.value("C:/foo;C:/bar;C:/baz") @current_resource.value("C:/foo/bar;C:/baz;C:/bar") @provider.modify_env expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar") end end end chef-12.3.0/spec/unit/provider/script_spec.rb0000644000004100000410000000716412520074675021160 0ustar www-datawww-data# # Author:: Adam Jacob (adam@opscode.com) # Copyright:: Copyright (c) 2009 Opscode # 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::Script, "action_run" do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) { new_resource = Chef::Resource::Script.new('run some perl code') new_resource.code "$| = 1; print 'i like beans'" new_resource.interpreter 'perl' new_resource } let(:provider) { Chef::Provider::Script.new(new_resource, run_context) } let(:tempfile) { Tempfile.open("rspec-provider-script") } before(:each) do allow(provider).to receive(:shell_out!).and_return(true) allow(provider).to receive(:script_file).and_return(tempfile) end context "#script_file" do it "creates a temporary file to store the script" do allow(provider).to receive(:script_file).and_call_original expect(provider.script_file).to be_an_instance_of(Tempfile) end end context "#unlink_script_file" do it "unlinks the tempfile" do tempfile_path = tempfile.path provider.unlink_script_file expect(File.exist?(tempfile_path)).to be false end end context "#set_owner_and_group" do it "sets the owner and group for the script file" do new_resource.user 'toor' new_resource.group 'wheel' expect(FileUtils).to receive(:chown).with('toor', 'wheel', tempfile.path) provider.set_owner_and_group end end context "with the script file set to the correct owner and group" do before do allow(provider).to receive(:set_owner_and_group) end describe "when writing the script to the file" do it "should put the contents of the script in the temp file" do allow(provider).to receive(:unlink_script_file) # stub to avoid remove provider.action_run expect(IO.read(tempfile.path)).to eq("$| = 1; print 'i like beans'\n") provider.unlink_script_file end it "closes before executing the script and unlinks it when finished" do tempfile_path = tempfile.path provider.action_run expect(tempfile).to be_closed expect(File.exist?(tempfile_path)).to be false end end describe "when running the script" do let (:default_opts) { {timeout: 3600, returns: 0, log_level: :info, log_tag: "script[run some perl code]", live_stream: STDOUT} } before do allow(STDOUT).to receive(:tty?).and_return(true) end it 'should set the command to "interpreter" "tempfile"' do expect(provider.command).to eq(%Q{"perl" "#{tempfile.path}"}) end it 'should call shell_out! with the command' do expect(provider).to receive(:shell_out!).with(provider.command, default_opts).and_return(true) provider.action_run end it "should set the command to 'interpreter flags tempfile'" do new_resource.flags '-f' expect(provider.command).to eq(%Q{"perl" -f "#{tempfile.path}"}) end end end end chef-12.3.0/spec/unit/provider/ifconfig_spec.rb0000644000004100000410000001522212520074675021432 0ustar www-datawww-data# # Author:: Prajakta Purohit (prajakta@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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) require 'spec_helper' require 'chef/exceptions' describe Chef::Provider::Ifconfig do before do @node = Chef::Node.new @cookbook_collection = Chef::CookbookCollection.new([]) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) #This new_resource can be called anything --> it is not the same as in ifconfig.rb @new_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) @new_resource.mask "255.255.254.0" @new_resource.metric "1" @new_resource.mtu "1500" @new_resource.device "eth0" @provider = Chef::Provider::Ifconfig.new(@new_resource, @run_context) @current_resource = Chef::Resource::Ifconfig.new("10.0.0.1", @run_context) status = double("Status", :exitstatus => 0) @provider.instance_variable_set("@status", status) @provider.current_resource = @current_resource end describe Chef::Provider::Ifconfig, "load_current_resource" do before do @status = double(:stdout => "", :exitstatus => 1) allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource end it "should track state of ifconfig failure." do expect(@provider.instance_variable_get("@status").exitstatus).not_to eq(0) end it "should thrown an exception when ifconfig fails" do @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error Chef::Exceptions::Ifconfig end end describe Chef::Provider::Ifconfig, "action_add" do it "should add an interface if it does not exist" do #@provider.stub(:run_command).and_return(true) allow(@provider).to receive(:load_current_resource) @current_resource.inet_addr nil command = "ifconfig eth0 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500" expect(@provider).to receive(:run_command).with(:command => command) expect(@provider).to receive(:generate_config) @provider.run_action(:add) expect(@new_resource).to be_updated end it "should not add an interface if it already exists" do allow(@provider).to receive(:load_current_resource) expect(@provider).not_to receive(:run_command) @current_resource.inet_addr "10.0.0.1" expect(@provider).to receive(:generate_config) @provider.run_action(:add) expect(@new_resource).not_to be_updated end #We are not testing this case with the assumption that anyone writing the cookbook would not make a typo == lo #it "should add a blank command if the #{@new_resource.device} == lo" do #end end describe Chef::Provider::Ifconfig, "action_enable" do it "should enable interface if does not exist" do allow(@provider).to receive(:load_current_resource) @current_resource.inet_addr nil command = "ifconfig eth0 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500" expect(@provider).to receive(:run_command).with(:command => command) expect(@provider).not_to receive(:generate_config) @provider.run_action(:enable) expect(@new_resource).to be_updated end it "should not enable interface if it already exists" do allow(@provider).to receive(:load_current_resource) expect(@provider).not_to receive(:run_command) @current_resource.inet_addr "10.0.0.1" expect(@provider).not_to receive(:generate_config) @provider.run_action(:enable) expect(@new_resource).not_to be_updated end end describe Chef::Provider::Ifconfig, "action_delete" do it "should delete interface if it exists" do allow(@provider).to receive(:load_current_resource) @current_resource.device "eth0" command = "ifconfig #{@new_resource.device} down" expect(@provider).to receive(:run_command).with(:command => command) expect(@provider).to receive(:delete_config) @provider.run_action(:delete) expect(@new_resource).to be_updated end it "should not delete interface if it does not exist" do allow(@provider).to receive(:load_current_resource) expect(@provider).not_to receive(:run_command) expect(@provider).to receive(:delete_config) @provider.run_action(:delete) expect(@new_resource).not_to be_updated end end describe Chef::Provider::Ifconfig, "action_disable" do it "should disable interface if it exists" do allow(@provider).to receive(:load_current_resource) @current_resource.device "eth0" command = "ifconfig #{@new_resource.device} down" expect(@provider).to receive(:run_command).with(:command => command) expect(@provider).not_to receive(:delete_config) @provider.run_action(:disable) expect(@new_resource).to be_updated end it "should not delete interface if it does not exist" do allow(@provider).to receive(:load_current_resource) expect(@provider).not_to receive(:run_command) expect(@provider).not_to receive(:delete_config) @provider.run_action(:disable) expect(@new_resource).not_to be_updated end end describe Chef::Provider::Ifconfig, "action_delete" do it "should delete interface of it exists" do allow(@provider).to receive(:load_current_resource) @current_resource.device "eth0" command = "ifconfig #{@new_resource.device} down" expect(@provider).to receive(:run_command).with(:command => command) expect(@provider).to receive(:delete_config) @provider.run_action(:delete) expect(@new_resource).to be_updated end it "should not delete interface if it does not exist" do # This is so that our fake values do not get overwritten allow(@provider).to receive(:load_current_resource) # This is so that nothing actually runs expect(@provider).not_to receive(:run_command) expect(@provider).to receive(:delete_config) @provider.run_action(:delete) expect(@new_resource).not_to be_updated end end end chef-12.3.0/spec/unit/provider/package_spec.rb0000644000004100000410000007004512520074675021245 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Provider::Package do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new('emacs') @current_resource = Chef::Resource::Package.new('emacs') @provider = Chef::Provider::Package.new(@new_resource, @run_context) @provider.current_resource = @current_resource @provider.candidate_version = "1.0" end describe "when installing a package" do before(:each) do @provider.current_resource = @current_resource allow(@provider).to receive(:install_package).and_return(true) end it "should raise a Chef::Exceptions::Package if no version is specified, and no candidate is available" do @provider.candidate_version = nil expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "should call preseed_package if a response_file is given" do @new_resource.response_file("foo") expect(@provider).to receive(:get_preseed_file).with( @new_resource.name, @provider.candidate_version ).and_return("/var/cache/preseed-test") expect(@provider).to receive(:preseed_package).with( "/var/cache/preseed-test" ).and_return(true) @provider.run_action(:install) end it "should not call preseed_package if a response_file is not given" do expect(@provider).not_to receive(:preseed_package) @provider.run_action(:install) end it "should install the package at the candidate_version if it is not already installed" do expect(@provider).to receive(:install_package).with( @new_resource.name, @provider.candidate_version ).and_return(true) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "should install the package at the version specified if it is not already installed" do @new_resource.version("1.0") expect(@provider).to receive(:install_package).with( @new_resource.name, @new_resource.version ).and_return(true) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "should install the package at the version specified if a different version is installed" do @new_resource.version("1.0") allow(@current_resource).to receive(:version).and_return("0.99") expect(@provider).to receive(:install_package).with( @new_resource.name, @new_resource.version ).and_return(true) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "should not install the package if it is already installed and no version is specified" do @current_resource.version("1.0") expect(@provider).not_to receive(:install_package) @provider.run_action(:install) expect(@new_resource).not_to be_updated_by_last_action end it "should not install the package if it is already installed at the version specified" do @current_resource.version("1.0") @new_resource.version("1.0") expect(@provider).not_to receive(:install_package) @provider.run_action(:install) expect(@new_resource).not_to be_updated_by_last_action end it "should call the candidate_version accessor only once if the package is already installed and no version is specified" do @current_resource.version("1.0") allow(@provider).to receive(:candidate_version).and_return("1.0") @provider.run_action(:install) end it "should call the candidate_version accessor only once if the package is already installed at the version specified" do @current_resource.version("1.0") @new_resource.version("1.0") @provider.run_action(:install) end it "should set the resource to updated if it installs the package" do @provider.run_action(:install) expect(@new_resource).to be_updated end end describe "when upgrading the package" do before(:each) do allow(@provider).to receive(:upgrade_package).and_return(true) end it "should upgrade the package if the current version is not the candidate version" do expect(@provider).to receive(:upgrade_package).with( @new_resource.name, @provider.candidate_version ).and_return(true) @provider.run_action(:upgrade) expect(@new_resource).to be_updated_by_last_action end it "should set the resource to updated if it installs the package" do @provider.run_action(:upgrade) expect(@new_resource).to be_updated end it "should not install the package if the current version is the candidate version" do @current_resource.version "1.0" expect(@provider).not_to receive(:upgrade_package) @provider.run_action(:upgrade) expect(@new_resource).not_to be_updated_by_last_action end it "should print the word 'uninstalled' if there was no original version" do allow(@current_resource).to receive(:version).and_return(nil) expect(Chef::Log).to receive(:info).with("package[emacs] upgraded emacs to 1.0") @provider.run_action(:upgrade) expect(@new_resource).to be_updated_by_last_action end it "should raise a Chef::Exceptions::Package if current version and candidate are nil" do allow(@current_resource).to receive(:version).and_return(nil) @provider.candidate_version = nil expect { @provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package) end it "should not install the package if candidate version is nil" do @current_resource.version "1.0" @provider.candidate_version = nil expect(@provider).not_to receive(:upgrade_package) @provider.run_action(:upgrade) expect(@new_resource).not_to be_updated_by_last_action end end describe "When removing the package" do before(:each) do allow(@provider).to receive(:remove_package).and_return(true) @current_resource.version '1.4.2' end it "should remove the package if it is installed" do expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with('emacs', nil) @provider.run_action(:remove) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should remove the package at a specific version if it is installed at that version" do @new_resource.version "1.4.2" expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with('emacs', '1.4.2') @provider.run_action(:remove) expect(@new_resource).to be_updated_by_last_action end it "should not remove the package at a specific version if it is not installed at that version" do @new_resource.version "1.0" expect(@provider).not_to be_removing_package expect(@provider).not_to receive(:remove_package) @provider.run_action(:remove) expect(@new_resource).not_to be_updated_by_last_action end it "should not remove the package if it is not installed" do expect(@provider).not_to receive(:remove_package) allow(@current_resource).to receive(:version).and_return(nil) @provider.run_action(:remove) expect(@new_resource).not_to be_updated_by_last_action end it "should set the resource to updated if it removes the package" do @provider.run_action(:remove) expect(@new_resource).to be_updated end end describe "When purging the package" do before(:each) do allow(@provider).to receive(:purge_package).and_return(true) @current_resource.version '1.4.2' end it "should purge the package if it is installed" do expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with('emacs', nil) @provider.run_action(:purge) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should purge the package at a specific version if it is installed at that version" do @new_resource.version "1.4.2" expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with('emacs', '1.4.2') @provider.run_action(:purge) expect(@new_resource).to be_updated_by_last_action end it "should not purge the package at a specific version if it is not installed at that version" do @new_resource.version "1.0" expect(@provider).not_to be_removing_package expect(@provider).not_to receive(:purge_package) @provider.run_action(:purge) expect(@new_resource).not_to be_updated_by_last_action end it "should not purge the package if it is not installed" do @current_resource.instance_variable_set(:@version, nil) expect(@provider).not_to be_removing_package expect(@provider).not_to receive(:purge_package) @provider.run_action(:purge) expect(@new_resource).not_to be_updated_by_last_action end it "should set the resource to updated if it purges the package" do @provider.run_action(:purge) expect(@new_resource).to be_updated end end describe "when reconfiguring the package" do before(:each) do allow(@provider).to receive(:reconfig_package).and_return(true) end it "should info log, reconfigure the package and update the resource" do allow(@current_resource).to receive(:version).and_return('1.0') allow(@new_resource).to receive(:response_file).and_return(true) expect(@provider).to receive(:get_preseed_file).and_return('/var/cache/preseed-test') allow(@provider).to receive(:preseed_package).and_return(true) allow(@provider).to receive(:reconfig_package).and_return(true) expect(Chef::Log).to receive(:info).with("package[emacs] reconfigured") expect(@provider).to receive(:reconfig_package) @provider.run_action(:reconfig) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should debug log and not reconfigure the package if the package is not installed" do allow(@current_resource).to receive(:version).and_return(nil) expect(Chef::Log).to receive(:debug).with("package[emacs] is NOT installed - nothing to do") expect(@provider).not_to receive(:reconfig_package) @provider.run_action(:reconfig) expect(@new_resource).not_to be_updated_by_last_action end it "should debug log and not reconfigure the package if no response_file is given" do allow(@current_resource).to receive(:version).and_return('1.0') allow(@new_resource).to receive(:response_file).and_return(nil) expect(Chef::Log).to receive(:debug).with("package[emacs] no response_file provided - nothing to do") expect(@provider).not_to receive(:reconfig_package) @provider.run_action(:reconfig) expect(@new_resource).not_to be_updated_by_last_action end it "should debug log and not reconfigure the package if the response_file has not changed" do allow(@current_resource).to receive(:version).and_return('1.0') allow(@new_resource).to receive(:response_file).and_return(true) expect(@provider).to receive(:get_preseed_file).and_return(false) allow(@provider).to receive(:preseed_package).and_return(false) expect(Chef::Log).to receive(:debug).with("package[emacs] preseeding has not changed - nothing to do") expect(@provider).not_to receive(:reconfig_package) @provider.run_action(:reconfig) expect(@new_resource).not_to be_updated_by_last_action end end describe "when running commands to be implemented by subclasses" do it "should raises UnsupportedAction for install" do expect { @provider.install_package('emacs', '1.4.2') }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should raises UnsupportedAction for upgrade" do expect { @provider.upgrade_package('emacs', '1.4.2') }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should raises UnsupportedAction for remove" do expect { @provider.remove_package('emacs', '1.4.2') }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should raises UnsupportedAction for purge" do expect { @provider.purge_package('emacs', '1.4.2') }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should raise UnsupportedAction for preseed_package" do preseed_file = "/tmp/sun-jdk-package-preseed-file.seed" expect { @provider.preseed_package(preseed_file) }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should raise UnsupportedAction for reconfig" do expect { @provider.reconfig_package('emacs', '1.4.2') }.to raise_error(Chef::Exceptions::UnsupportedAction) end end describe "when given a response file" do before(:each) do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) cl.load_cookbooks @cookbook_collection = Chef::CookbookCollection.new(cl) @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @provider.run_context = @run_context @node.automatic_attrs[:platform] = 'PLATFORM: just testing' @node.automatic_attrs[:platform_version] = 'PLATFORM VERSION: just testing' @new_resource.response_file('java.response') @new_resource.cookbook_name = 'java' end describe "creating the cookbook file resource to fetch the response file" do before do expect(Chef::FileCache).to receive(:create_cache_path).with('preseed/java').and_return("/tmp/preseed/java") end it "sets the preseed resource's runcontext to its own run context" do allow(Chef::FileCache).to receive(:create_cache_path).and_return("/tmp/preseed/java") expect(@provider.preseed_resource('java', '6').run_context).not_to be_nil expect(@provider.preseed_resource('java', '6').run_context).to equal(@provider.run_context) end it "should set the cookbook name of the remote file to the new resources cookbook name" do expect(@provider.preseed_resource('java', '6').cookbook_name).to eq('java') end it "should set remote files source to the new resources response file" do expect(@provider.preseed_resource('java', '6').source).to eq('java.response') end it "should never back up the cached response file" do expect(@provider.preseed_resource('java', '6').backup).to be_falsey end it "sets the install path of the resource to $file_cache/$cookbook/$pkg_name-$pkg_version.seed" do expect(@provider.preseed_resource('java', '6').path).to eq('/tmp/preseed/java/java-6.seed') end end describe "when installing the preseed file to the cache location" do before do @node.automatic_attrs[:platform] = :just_testing @node.automatic_attrs[:platform_version] = :just_testing @response_file_destination = Dir.tmpdir + '/preseed--java--java-6.seed' @response_file_resource = Chef::Resource::CookbookFile.new(@response_file_destination, @run_context) @response_file_resource.cookbook_name = 'java' @response_file_resource.backup(false) @response_file_resource.source('java.response') expect(@provider).to receive(:preseed_resource).with('java', '6').and_return(@response_file_resource) end after do FileUtils.rm(@response_file_destination) if ::File.exist?(@response_file_destination) end it "creates the preseed file in the cache" do expect(@response_file_resource).to receive(:run_action).with(:create) @provider.get_preseed_file("java", "6") end it "returns the path to the response file if the response file was updated" do expect(@provider.get_preseed_file("java", "6")).to eq(@response_file_destination) end it "should return false if the response file has not been updated" do @response_file_resource.updated_by_last_action(false) expect(@response_file_resource).not_to be_updated_by_last_action # don't let the response_file_resource set updated to true expect(@response_file_resource).to receive(:run_action).with(:create) expect(@provider.get_preseed_file("java", "6")).to be(false) end end end end describe "Chef::Provider::Package - Multi" do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new(['emacs', 'vi']) @current_resource = Chef::Resource::Package.new(['emacs', 'vi']) @provider = Chef::Provider::Package.new(@new_resource, @run_context) @provider.current_resource = @current_resource @provider.candidate_version = ['1.0', '6.2'] end describe "when installing multiple packages" do before(:each) do @provider.current_resource = @current_resource allow(@provider).to receive(:install_package).and_return(true) end it "installs the candidate versions when none are installed" do expect(@provider).to receive(:install_package).with( ["emacs", "vi"], ["1.0", "6.2"] ).and_return(true) @provider.run_action(:install) expect(@new_resource).to be_updated end it "installs the candidate versions when some are installed" do expect(@provider).to receive(:install_package).with( [ 'vi' ], [ '6.2' ] ).and_return(true) @current_resource.version(['1.0', nil]) @provider.run_action(:install) expect(@new_resource).to be_updated end it "installs the specified version when some are out of date" do @current_resource.version(['1.0', '6.2']) @new_resource.version(['1.0', '6.1']) @provider.run_action(:install) expect(@new_resource).to be_updated end it "does not install any version if all are installed at the right version" do @current_resource.version(['1.0', '6.2']) @new_resource.version(['1.0', '6.2']) @provider.run_action(:install) expect(@new_resource).not_to be_updated_by_last_action end it "does not install any version if all are installed, and no version was specified" do @current_resource.version(['1.0', '6.2']) @provider.run_action(:install) expect(@new_resource).not_to be_updated_by_last_action end it "raises an exception if both are not installed and no caondidates are available" do @current_resource.version([nil, nil]) @provider.candidate_version = [nil, nil] expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "raises an exception if one is not installed and no candidates are available" do @current_resource.version(['1.0', nil]) @provider.candidate_version = ['1.0', nil] expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "does not raise an exception if the packages are installed or have a candidate" do @current_resource.version(['1.0', nil]) @provider.candidate_version = [nil, '6.2'] expect { @provider.run_action(:install) }.not_to raise_error end it "raises an exception if an explicit version is asked for, an old version is installed, but no candidate" do @new_resource.version ['1.0', '6.2'] @current_resource.version(['1.0', '6.1']) @provider.candidate_version = ['1.0', nil] expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "does not raise an exception if an explicit version is asked for, and is installed, but no candidate" do @new_resource.version ['1.0', '6.2'] @current_resource.version(['1.0', '6.2']) @provider.candidate_version = ['1.0', nil] expect { @provider.run_action(:install) }.not_to raise_error end it "raise an exception if an explicit version is asked for, and is not installed, and no candidate" do @new_resource.version ['1.0', '6.2'] @current_resource.version(['1.0', nil]) @provider.candidate_version = ['1.0', nil] expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "does not raise an exception if an explicit version is asked for, and is not installed, and there is a candidate" do @new_resource.version ['1.0', '6.2'] @current_resource.version(['1.0', nil]) @provider.candidate_version = ['1.0', '6.2'] expect { @provider.run_action(:install) }.not_to raise_error end end describe "when upgrading multiple packages" do before(:each) do @provider.current_resource = @current_resource allow(@provider).to receive(:upgrade_package).and_return(true) end it "should upgrade the package if the current versions are not the candidate version" do @current_resource.version ['0.9', '6.1'] expect(@provider).to receive(:upgrade_package).with( @new_resource.package_name, @provider.candidate_version ).and_return(true) @provider.run_action(:upgrade) expect(@new_resource).to be_updated_by_last_action end it "should upgrade the package if some of current versions are not the candidate versions" do @current_resource.version ['1.0', '6.1'] expect(@provider).to receive(:upgrade_package).with( ["vi"], ["6.2"] ).and_return(true) @provider.run_action(:upgrade) expect(@new_resource).to be_updated_by_last_action end it "should not install the package if the current versions are the candidate version" do @current_resource.version ['1.0', '6.2'] expect(@provider).not_to receive(:upgrade_package) @provider.run_action(:upgrade) expect(@new_resource).not_to be_updated_by_last_action end it "should raise an exception if both are not installed and no caondidates are available" do @current_resource.version([nil, nil]) @provider.candidate_version = [nil, nil] expect { @provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package) end it "should raise an exception if one is not installed and no candidates are available" do @current_resource.version(['1.0', nil]) @provider.candidate_version = ['1.0', nil] expect { @provider.run_action(:upgrade) }.to raise_error(Chef::Exceptions::Package) end it "should not raise an exception if the packages are installed or have a candidate" do @current_resource.version(['1.0', nil]) @provider.candidate_version = [nil, '6.2'] expect { @provider.run_action(:upgrade) }.not_to raise_error end it "should not raise an exception if the packages are installed or have a candidate" do @current_resource.version(['1.0', nil]) @provider.candidate_version = [nil, '6.2'] expect { @provider.run_action(:upgrade) }.not_to raise_error end end describe "When removing multiple packages " do before(:each) do allow(@provider).to receive(:remove_package).and_return(true) @current_resource.version ['1.0', '6.2'] end it "should remove the packages if all are installed" do expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with(['emacs', 'vi'], nil) @provider.run_action(:remove) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should remove the packages if some are installed" do @current_resource.version ['1.0', nil] expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with(['emacs', 'vi'], nil) @provider.run_action(:remove) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should remove the packages at a specific version if they are installed at that version" do @new_resource.version ['1.0', '6.2'] expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with(['emacs', 'vi'], ['1.0', '6.2']) @provider.run_action(:remove) expect(@new_resource).to be_updated_by_last_action end it "should remove the packages at a specific version any are is installed at that version" do @new_resource.version ['0.5', '6.2'] expect(@provider).to be_removing_package expect(@provider).to receive(:remove_package).with(['emacs', 'vi'], ['0.5', '6.2']) @provider.run_action(:remove) expect(@new_resource).to be_updated_by_last_action end it "should not remove the packages at a specific version if they are not installed at that version" do @new_resource.version ['0.5', '6.0'] expect(@provider).not_to be_removing_package expect(@provider).not_to receive(:remove_package) @provider.run_action(:remove) expect(@new_resource).not_to be_updated_by_last_action end it "should not remove the packages if they are not installed" do expect(@provider).not_to receive(:remove_package) allow(@current_resource).to receive(:version).and_return(nil) @provider.run_action(:remove) expect(@new_resource).not_to be_updated_by_last_action end end describe "When purging multiple packages " do before(:each) do allow(@provider).to receive(:purge_package).and_return(true) @current_resource.version ['1.0', '6.2'] end it "should purge the packages if all are installed" do expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with(['emacs', 'vi'], nil) @provider.run_action(:purge) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should purge the packages if some are installed" do @current_resource.version ['1.0', nil] expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with(['emacs', 'vi'], nil) @provider.run_action(:purge) expect(@new_resource).to be_updated expect(@new_resource).to be_updated_by_last_action end it "should purge the packages at a specific version if they are installed at that version" do @new_resource.version ['1.0', '6.2'] expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with(['emacs', 'vi'], ['1.0', '6.2']) @provider.run_action(:purge) expect(@new_resource).to be_updated_by_last_action end it "should purge the packages at a specific version any are is installed at that version" do @new_resource.version ['0.5', '6.2'] expect(@provider).to be_removing_package expect(@provider).to receive(:purge_package).with(['emacs', 'vi'], ['0.5', '6.2']) @provider.run_action(:purge) expect(@new_resource).to be_updated_by_last_action end it "should not purge the packages at a specific version if they are not installed at that version" do @new_resource.version ['0.5', '6.0'] expect(@provider).not_to be_removing_package expect(@provider).not_to receive(:purge_package) @provider.run_action(:purge) expect(@new_resource).not_to be_updated_by_last_action end it "should not purge the packages if they are not installed" do expect(@provider).not_to receive(:purge_package) allow(@current_resource).to receive(:version).and_return(nil) @provider.run_action(:purge) expect(@new_resource).not_to be_updated_by_last_action end end end chef-12.3.0/spec/unit/provider/template/0000755000004100000410000000000012520074675020120 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/template/content_spec.rb0000644000004100000410000000563212520074675023137 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::Template::Content do let(:new_resource) do double("Chef::Resource::Template (new)", :cookbook_name => 'openldap', :source => 'openldap_stuff.conf.erb', :local => false, :cookbook => nil, :variables => {}, :inline_helper_blocks => {}, :inline_helper_modules => [], :helper_modules => []) end let(:rendered_file_location) { Dir.tmpdir + '/openldap_stuff.conf' } let(:run_context) do cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) node = Chef::Node.new double("Chef::Resource::RunContext", :node => node, :cookbook_collection => cookbook_collection) end let(:content) do current_resource = double("Chef::Resource::Template (current)") Chef::Provider::Template::Content.new(new_resource, current_resource, run_context) end after do FileUtils.rm(rendered_file_location) if ::File.exist?(rendered_file_location) end it "finds the template file in the cookbook cache if it isn't local" do expect(content.template_location).to eq(CHEF_SPEC_DATA + '/cookbooks/openldap/templates/default/openldap_stuff.conf.erb') end it "finds the template file locally if it is local" do allow(new_resource).to receive(:local).and_return(true) allow(new_resource).to receive(:source).and_return('/tmp/its_on_disk.erb') expect(content.template_location).to eq('/tmp/its_on_disk.erb') end it "should use the cookbook name if defined in the template resource" do allow(new_resource).to receive(:cookbook_name).and_return('apache2') allow(new_resource).to receive(:cookbook).and_return('openldap') allow(new_resource).to receive(:source).and_return("test.erb") expect(content.template_location).to eq(CHEF_SPEC_DATA + '/cookbooks/openldap/templates/default/test.erb') end it "creates the template with the rendered content" do run_context.node.normal[:slappiness] = "a warm gun" expect(IO.read(content.tempfile.path)).to eq("slappiness is a warm gun") end end chef-12.3.0/spec/unit/provider/cookbook_file_spec.rb0000644000004100000410000000346212520074675022456 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2009-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 'ostruct' require 'support/shared/unit/provider/file' describe Chef::Provider::CookbookFile do let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } # Subject let(:provider) do provider = described_class.new(resource, run_context) allow(provider).to receive(:content).and_return(content) provider end let(:resource) do resource = Chef::Resource::CookbookFile.new("seattle", @run_context) resource.path(resource_path) resource.cookbook_name = 'apache2' resource end let(:content) do content = double('Chef::Provider::CookbookFile::Content') end it_behaves_like Chef::Provider::File it_behaves_like "a file provider with source field" end chef-12.3.0/spec/unit/provider/remote_file/0000755000004100000410000000000012520074675020577 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/remote_file/fetcher_spec.rb0000644000004100000410000000513512520074675023562 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::RemoteFile::Fetcher do let(:current_resource) { double("current resource") } let(:new_resource) { double("new resource") } let(:fetcher_instance) { double("fetcher") } describe "when passed an http url" do let(:uri) { double("uri", :scheme => "http" ) } before do expect(Chef::Provider::RemoteFile::HTTP).to receive(:new).and_return(fetcher_instance) end it "returns an http fetcher" do expect(described_class.for_resource(uri, new_resource, current_resource)).to eq(fetcher_instance) end end describe "when passed an https url" do let(:uri) { double("uri", :scheme => "https" ) } before do expect(Chef::Provider::RemoteFile::HTTP).to receive(:new).and_return(fetcher_instance) end it "returns an http fetcher" do expect(described_class.for_resource(uri, new_resource, current_resource)).to eq(fetcher_instance) end end describe "when passed an ftp url" do let(:uri) { double("uri", :scheme => "ftp" ) } before do expect(Chef::Provider::RemoteFile::FTP).to receive(:new).and_return(fetcher_instance) end it "returns an ftp fetcher" do expect(described_class.for_resource(uri, new_resource, current_resource)).to eq(fetcher_instance) end end describe "when passed a file url" do let(:uri) { double("uri", :scheme => "file" ) } before do expect(Chef::Provider::RemoteFile::LocalFile).to receive(:new).and_return(fetcher_instance) end it "returns a localfile fetcher" do expect(described_class.for_resource(uri, new_resource, current_resource)).to eq(fetcher_instance) end end describe "when passed a url we do not recognize" do let(:uri) { double("uri", :scheme => "xyzzy" ) } it "throws an ArgumentError exception" do expect { described_class.for_resource(uri, new_resource, current_resource) }.to raise_error(ArgumentError) end end end chef-12.3.0/spec/unit/provider/remote_file/content_spec.rb0000644000004100000410000002235112520074675023613 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::RemoteFile::Content do # # mock setup # let(:current_resource) do Chef::Resource::RemoteFile.new("remote-file-content-spec (current resource)") end let(:source) { [ "http://opscode.com/seattle.txt" ] } let(:new_resource) do r = Chef::Resource::RemoteFile.new("remote-file-content-spec (current resource)") r.source(source) r end let(:run_context) { double("Chef::RunContext") } # # subject # let(:content) do Chef::Provider::RemoteFile::Content.new(new_resource, current_resource, run_context) end describe "when the checksum of the current_resource matches the checksum set on the resource" do before do allow(new_resource).to receive(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa") allow(current_resource).to receive(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa") end it "should return nil for the tempfile" do expect(content.tempfile).to be_nil end it "should not call any fetcher" do expect(Chef::Provider::RemoteFile::Fetcher).not_to receive(:for_resource) end end describe "when the checksum of the current_resource is a partial match for the checksum set on the resource" do before do allow(new_resource).to receive(:checksum).and_return("0fd012fd") allow(current_resource).to receive(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa") end it "should return nil for the tempfile" do expect(content.tempfile).to be_nil end it "should not call any fetcher" do expect(Chef::Provider::RemoteFile::Fetcher).not_to receive(:for_resource) end end shared_examples_for "the resource needs fetching" do before do # FIXME: test one or the other nil, test both not nil and not equal, abuse the regexp a little @uri = double("URI") expect(URI).to receive(:parse).with(new_resource.source[0]).and_return(@uri) end describe "when the fetcher returns nil for the tempfile" do before do http_fetcher = double("Chef::Provider::RemoteFile::HTTP", :fetch => nil) expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri, new_resource, current_resource).and_return(http_fetcher) end it "should return nil for the tempfile" do expect(content.tempfile).to be_nil end end describe "when the fetcher returns a valid tempfile" do let(:mtime) { Time.now } let(:tempfile) { double("Tempfile") } let(:http_fetcher) { double("Chef::Provider::RemoteFile::HTTP", :fetch => tempfile) } before do expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri, new_resource, current_resource).and_return(http_fetcher) end it "should return the tempfile object to the caller" do expect(content.tempfile).to eq(tempfile) end end end describe "when the checksum are both nil" do before do expect(new_resource.checksum).to be_nil expect(current_resource.checksum).to be_nil end it_behaves_like "the resource needs fetching" end describe "when the current_resource checksum is nil" do before do allow(new_resource).to receive(:checksum).and_return("fd012fd") allow(current_resource).to receive(:checksum).and_return(nil) end it_behaves_like "the resource needs fetching" end describe "when the new_resource checksum is nil" do before do allow(new_resource).to receive(:checksum).and_return(nil) allow(current_resource).to receive(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa") end it_behaves_like "the resource needs fetching" end describe "when the checksums are a partial match, but not to the leading portion" do before do allow(new_resource).to receive(:checksum).and_return("fd012fd") allow(current_resource).to receive(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa") end it_behaves_like "the resource needs fetching" end describe "when the fetcher throws an exception" do before do allow(new_resource).to receive(:checksum).and_return(nil) allow(current_resource).to receive(:checksum).and_return(nil) @uri = double("URI") expect(URI).to receive(:parse).with(new_resource.source[0]).and_return(@uri) http_fetcher = double("Chef::Provider::RemoteFile::HTTP") expect(http_fetcher).to receive(:fetch).and_raise(Errno::ECONNREFUSED) expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri, new_resource, current_resource).and_return(http_fetcher) end it "should propagate the error back to the caller" do expect { content.tempfile }.to raise_error(Errno::ECONNREFUSED) end end describe "when there is an array of sources and the first fails" do # https://github.com/opscode/chef/pull/1358#issuecomment-40853299 def create_exception(exception_class) if [ Net::HTTPServerException, Net::HTTPFatalError ].include? exception_class exception_class.new("message", {"something" => 1}) else exception_class.new end end let(:source) { [ "http://opscode.com/seattle.txt", "http://opscode.com/nyc.txt" ] } ### Test each exception we care about and make sure they all behave properly [ SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPServerException, Net::HTTPFatalError, Net::FTPError ].each do |exception| describe "with an exception of #{exception}" do before do allow(new_resource).to receive(:checksum).and_return(nil) allow(current_resource).to receive(:checksum).and_return(nil) @uri0 = double("URI0") @uri1 = double("URI1") expect(URI).to receive(:parse).with(new_resource.source[0]).and_return(@uri0) expect(URI).to receive(:parse).with(new_resource.source[1]).and_return(@uri1) @http_fetcher_throws_exception = double("Chef::Provider::RemoteFile::HTTP") expect(@http_fetcher_throws_exception).to receive(:fetch).at_least(:once).and_raise(create_exception(exception)) expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri0, new_resource, current_resource).and_return(@http_fetcher_throws_exception) end describe "the second url should succeed" do before do @tempfile = double("Tempfile") mtime = Time.now http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", :fetch => @tempfile) expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri1, new_resource, current_resource).and_return(http_fetcher_works) end it "should return a valid tempfile" do expect(content.tempfile).to eq(@tempfile) end it "should not mutate the new_resource" do content.tempfile expect(new_resource.source.length).to eq(2) end end describe "when both urls fail" do before do expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri1, new_resource, current_resource).and_return(@http_fetcher_throws_exception) end it "should propagate the error back to the caller" do expect { content.tempfile }.to raise_error(exception) end end end end end describe "when there is an array of sources and the first succeeds" do let(:source) { [ "http://opscode.com/seattle.txt", "http://opscode.com/nyc.txt" ] } before do allow(new_resource).to receive(:checksum).and_return(nil) allow(current_resource).to receive(:checksum).and_return(nil) @uri0 = double("URI0") expect(URI).to receive(:parse).with(new_resource.source[0]).and_return(@uri0) expect(URI).not_to receive(:parse).with(new_resource.source[1]) @tempfile = double("Tempfile") mtime = Time.now http_fetcher_works = double("Chef::Provider::RemoteFile::HTTP", :fetch => @tempfile) expect(Chef::Provider::RemoteFile::Fetcher).to receive(:for_resource).with(@uri0, new_resource, current_resource).and_return(http_fetcher_works) end it "should return a valid tempfile" do expect(content.tempfile).to eq(@tempfile) end it "should not mutate the new_resource" do content.tempfile expect(new_resource.source.length).to eq(2) end end end chef-12.3.0/spec/unit/provider/remote_file/cache_control_data_spec.rb0000644000004100000410000001752512520074675025744 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'uri' CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH = 64 CACHE_FILE_MD5_HEX_LENGTH = 32 CACHE_FILE_JSON_FILE_EXTENSION_LENGTH = 5 CACHE_FILE_PATH_LIMIT = CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH + 1 + CACHE_FILE_MD5_HEX_LENGTH + CACHE_FILE_JSON_FILE_EXTENSION_LENGTH # {friendly}-{md5hex}.json == 102 describe Chef::Provider::RemoteFile::CacheControlData do let(:uri) { URI.parse("http://www.google.com/robots.txt") } subject(:cache_control_data) do Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, current_file_checksum) end let(:cache_path) { "remote_file/http___www_google_com_robots_txt-9839677abeeadf0691026e0cabca2339.json" } # the checksum of the file we have on disk already let(:current_file_checksum) { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } context "when loading data for an unknown URI" do before do expect(Chef::FileCache).to receive(:load).with(cache_path).and_raise(Chef::Exceptions::FileNotFound, "nope") end context "and there is no current copy of the file" do let(:current_file_checksum) { nil } it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end end it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end context "and the URI contains a password" do let(:uri) { URI.parse("http://bob:password@example.org/") } let(:cache_path) { "remote_file/http___bob_XXXX_example_org_-f121caacb74c05a35bcefdf578ed5fc9.json" } it "loads the cache data from a path based on a sanitized URI" do Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, current_file_checksum) end end end describe "when loading data for a known URI" do # the checksum of the file last we fetched it. let(:last_fetched_checksum) { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } let(:etag) { "\"a-strong-identifier\"" } let(:mtime) { "Tue, 21 May 2013 19:19:23 GMT" } let(:cache_json_data) do cache = {} cache["etag"] = etag cache["mtime"] = mtime cache["checksum"] = last_fetched_checksum Chef::JSONCompat.to_json(cache) end before do expect(Chef::FileCache).to receive(:load).with(cache_path).and_return(cache_json_data) end context "and there is no on-disk copy of the file" do let(:current_file_checksum) { nil } it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end end context "and the cached checksum does not match the on-disk copy" do let(:current_file_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" } it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end end context "and the cached checksum matches the on-disk copy" do it "populates the cache control data" do expect(cache_control_data.etag).to eq(etag) expect(cache_control_data.mtime).to eq(mtime) end end context "and the cached checksum data is corrupted" do let(:cache_json_data) { '{"foo",,"bar" []}' } it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end context "and it still is valid JSON" do let(:cache_json_data) { '' } it "returns empty cache control data" do expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil end end end end describe "when saving to disk" do let(:etag) { "\"a-strong-identifier\"" } let(:mtime) { "Tue, 21 May 2013 19:19:23 GMT" } let(:fetched_file_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" } let(:expected_serialization_data) do data = {} data["etag"] = etag data["mtime"] = mtime data["checksum"] = fetched_file_checksum data end before do cache_control_data.etag = etag cache_control_data.mtime = mtime cache_control_data.checksum = fetched_file_checksum end it "serializes its attributes to JSON" do # we have to test this separately because ruby 1.8 hash order is unstable # so we can't count on the order of the keys in the json format. json_data = cache_control_data.json_data expect(Chef::JSONCompat.from_json(json_data)).to eq(expected_serialization_data) end it "writes data to the cache" do json_data = cache_control_data.json_data expect(Chef::FileCache).to receive(:store).with(cache_path, json_data) cache_control_data.save end context "and the URI contains a password" do let(:uri) { URI.parse("http://bob:password@example.org/") } let(:cache_path) { "remote_file/http___bob_XXXX_example_org_-f121caacb74c05a35bcefdf578ed5fc9.json" } it "writes the data to the cache with a sanitized path name" do json_data = cache_control_data.json_data expect(Chef::FileCache).to receive(:store).with(cache_path, json_data) cache_control_data.save end end # Cover the very long remote file path case -- see CHEF-4422 where # local cache file names generated from the long uri exceeded # local file system path limits resulting in exceptions from # file system API's on both Windows and Unix systems. context "and the URI results in a file cache path that exceeds #{CACHE_FILE_PATH_LIMIT} characters in length" do let(:long_remote_path) { "http://www.bing.com/" + ('0' * (CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH * 2 )) } let(:uri) { URI.parse(long_remote_path) } let(:truncated_remote_uri) { URI.parse(long_remote_path[0...CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH]) } let(:truncated_file_cache_path) do cache_control_data_truncated = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(truncated_remote_uri, current_file_checksum) cache_control_data_truncated.send('sanitized_cache_file_basename')[0...CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH] end it "truncates the file cache path to 102 characters" do normalized_cache_path = cache_control_data.send('sanitized_cache_file_basename') expect(Chef::FileCache).to receive(:store).with("remote_file/" + normalized_cache_path, cache_control_data.json_data) cache_control_data.save expect(normalized_cache_path.length).to eq(CACHE_FILE_PATH_LIMIT) end it "uses a file cache path that starts with the first #{CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH} characters of the URI" do normalized_cache_path = cache_control_data.send('sanitized_cache_file_basename') expect(truncated_file_cache_path.length).to eq(CACHE_FILE_TRUNCATED_FRIENDLY_FILE_NAME_LENGTH) expect(normalized_cache_path.start_with?(truncated_file_cache_path)).to eq(true) end end end end chef-12.3.0/spec/unit/provider/remote_file/local_file_spec.rb0000644000004100000410000000547112520074675024236 0ustar www-datawww-data# # Author:: Jesse Campbell () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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::RemoteFile::LocalFile do let(:uri) { URI.parse("file:///nyan_cat.png") } let(:new_resource) { Chef::Resource::RemoteFile.new("local file backend test (new_resource)") } let(:current_resource) { Chef::Resource::RemoteFile.new("local file backend test (current_resource)") } subject(:fetcher) { Chef::Provider::RemoteFile::LocalFile.new(uri, new_resource, current_resource) } context "when parsing source path" do describe "when given local unix path" do let(:uri) { URI.parse("file:///nyan_cat.png") } it "returns a correct unix path" do expect(fetcher.fix_windows_path(uri.path)).to eq("/nyan_cat.png") end end describe "when given local windows path" do let(:uri) { URI.parse("file:///z:/windows/path/file.txt") } it "returns a valid windows local path" do expect(fetcher.fix_windows_path(uri.path)).to eq("z:/windows/path/file.txt") end end describe "when given unc windows path" do let(:uri) { URI.parse("file:////server/share/windows/path/file.txt") } it "returns a valid windows unc path" do expect(fetcher.fix_windows_path(uri.path)).to eq("//server/share/windows/path/file.txt") end end end context "when first created" do it "stores the uri it is passed" do expect(fetcher.uri).to eq(uri) end it "stores the new_resource" do expect(fetcher.new_resource).to eq(new_resource) end end describe "when fetching the object" do let(:tempfile) { double("Tempfile", :path => "/tmp/foo/bar/nyan.png", :close => nil) } let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", :tempfile => tempfile) } before do current_resource.source("file:///nyan_cat.png") end it "stages the local file to a temporary file" do expect(Chef::FileContentManagement::Tempfile).to receive(:new).with(new_resource).and_return(chef_tempfile) expect(::FileUtils).to receive(:cp).with(uri.path, tempfile.path) expect(tempfile).to receive(:close) result = fetcher.fetch expect(result).to eq(tempfile) end end end chef-12.3.0/spec/unit/provider/remote_file/http_spec.rb0000644000004100000410000002532312520074675023122 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2013 Lamont Granquist # 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::RemoteFile::HTTP do let(:uri) { URI.parse("http://opscode.com/seattle.txt") } let(:existing_file_source) { nil } let(:current_resource_checksum) { "41e78735319af11327e9d2ca8535ea1c191e5ac1f76bb08d88fe6c3f93a8c8e5" } let(:current_resource) do current_resource = Chef::Resource::RemoteFile.new("/tmp/foo.txt") current_resource.source(existing_file_source) if existing_file_source current_resource.checksum(current_resource_checksum) current_resource end let(:new_resource) do Chef::Resource::RemoteFile.new("/tmp/foo.txt") end subject(:fetcher) do Chef::Provider::RemoteFile::HTTP.new(uri, new_resource, current_resource) end let(:cache_control_data) { Chef::Provider::RemoteFile::CacheControlData.new(uri) } describe "generating cache control headers" do context "and there is no valid cache control data for this URI on disk" do before do expect(Chef::Provider::RemoteFile::CacheControlData).to receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data) end it "does not add conditional GET headers" do expect(fetcher.conditional_get_headers).to eq({}) end context "and the resource specifies custom headers" do before do new_resource.headers("x-myapp-header" => "custom-header-value") end it "has the user-specified custom headers" do expect(fetcher.headers).to eq({"x-myapp-header" => "custom-header-value"}) end end end context "and the cache control data matches the existing file" do # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 let(:etag) { "\"a-strong-unique-identifier\"" } # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 let(:mtime) { "Tue, 21 May 2013 19:19:23 GMT" } before do cache_control_data.etag = etag cache_control_data.mtime = mtime expect(Chef::Provider::RemoteFile::CacheControlData).to receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data) end context "and no conditional get features are enabled" do before do new_resource.use_conditional_get(false) end it "does not add headers to the request" do expect(fetcher.headers).to eq({}) end end context "and conditional get is enabled" do before do new_resource.use_conditional_get(true) end it "adds If-None-Match and If-Modified-Since headers to the request" do headers = fetcher.headers expect(headers["if-none-match"]).to eq(etag) expect(headers["if-modified-since"]).to eq(mtime) end context "and custom headers are provided" do before do new_resource.headers("x-myapp-header" => "app-specific-header", "if-none-match" => "custom-etag", "if-modified-since" => "custom-last-modified") end it "preserves non-conflicting headers" do expect(fetcher.headers["x-myapp-header"]).to eq("app-specific-header") end it "prefers user-supplied cache control headers" do headers = fetcher.headers expect(headers["if-none-match"]).to eq("custom-etag") expect(headers["if-modified-since"]).to eq("custom-last-modified") end end end context "and etag support is enabled" do before do new_resource.use_conditional_get(false) new_resource.use_etags(true) end it "only adds If-None-Match headers to the request" do headers = fetcher.headers expect(headers["if-none-match"]).to eq(etag) expect(headers).not_to have_key("if-modified-since") end end context "and mtime support is enabled" do before do new_resource.use_conditional_get(false) new_resource.use_last_modified(true) end it "only adds If-Modified-Since headers to the request" do headers = fetcher.headers expect(headers["if-modified-since"]).to eq(mtime) expect(headers).not_to have_key("if-none-match") end end end end describe "when fetching the uri" do let(:expected_http_opts) { {} } let(:expected_http_args) { [uri, expected_http_opts] } let(:tempfile_path) { "/tmp/chef-mock-tempfile-abc123" } let(:tempfile) { double(Tempfile, :path => tempfile_path, :close => nil) } let(:last_response) { {} } let(:rest) do rest = double(Chef::HTTP::Simple) allow(rest).to receive(:streaming_request).and_return(tempfile) allow(rest).to receive(:last_response).and_return(last_response) rest end before do new_resource.headers({}) new_resource.use_last_modified(false) expect(Chef::Provider::RemoteFile::CacheControlData).to receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data) expect(Chef::HTTP::Simple).to receive(:new).with(*expected_http_args).and_return(rest) end describe "and the request does not return new content" do it "should return a nil tempfile for a 304 HTTPNotModifed" do # Streaming request returns nil for 304 errors allow(rest).to receive(:streaming_request).and_return(nil) expect(fetcher.fetch).to be_nil end end describe "and the request returns new content" do let(:fetched_content_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" } before do expect(cache_control_data).to receive(:save) expect(Chef::Digester).to receive(:checksum_for_file).with(tempfile_path).and_return(fetched_content_checksum) end it "should return a tempfile" do result = fetcher.fetch expect(result).to eq(tempfile) expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil expect(cache_control_data.checksum).to eq(fetched_content_checksum) end context "and the response does not contain an etag" do let(:last_response) { {"etag" => nil} } it "does not include an etag in the result" do fetcher.fetch expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil expect(cache_control_data.checksum).to eq(fetched_content_checksum) end end context "and the response has an etag header" do let(:last_response) { {"etag" => "abc123"} } it "includes the etag value in the response" do fetcher.fetch expect(cache_control_data.etag).to eq("abc123") expect(cache_control_data.mtime).to be_nil expect(cache_control_data.checksum).to eq(fetched_content_checksum) end end context "and the response has no Date or Last-Modified header" do let(:last_response) { {"date" => nil, "last_modified" => nil} } it "does not set an mtime in the result" do # RFC 2616 suggests that servers that do not set a Date header do not # have a reliable clock, so no use in making them deal with dates. fetcher.fetch expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil expect(cache_control_data.checksum).to eq(fetched_content_checksum) end end context "and the response has a Last-Modified header" do let(:last_response) do # Last-Modified should be preferred to Date if both are set {"date" => "Fri, 17 May 2013 23:23:23 GMT", "last_modified" => "Fri, 17 May 2013 11:11:11 GMT"} end it "sets the mtime to the Last-Modified time in the response" do fetcher.fetch expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to eq(last_response["last_modified"]) end end context "and the response has a Date header but no Last-Modified header" do let(:last_response) do {"date" => "Fri, 17 May 2013 23:23:23 GMT", "last_modified" => nil} end it "sets the mtime to the Date in the response" do fetcher.fetch expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to eq(last_response["date"]) expect(cache_control_data.checksum).to eq(fetched_content_checksum) end end context "and the target file is a tarball [CHEF-3140]" do let(:uri) { URI.parse("http://opscode.com/tarball.tgz") } let(:expected_http_opts) { {:disable_gzip => true} } # CHEF-3140 # Some servers return tarballs as content type tar and encoding gzip, which # is totally wrong. When this happens and gzip isn't disabled, Chef::HTTP::Simple # will decompress the file for you, which is not at all what you expected # to happen (you end up with an uncomressed tar archive instead of the # gzipped tar archive you expected). To work around this behavior, we # detect when users are fetching gzipped files and turn off gzip in # Chef::HTTP::Simple. it "should disable gzip compression in the client" do # Before block in the parent context has set an expectation on # Chef::HTTP::Simple.new() being called with expected arguments. Here we fufil # that expectation, so that we can explicitly set it for this test. # This is intended to provide insurance that refactoring of the parent # context does not negate the value of this particular example. Chef::HTTP::Simple.new(*expected_http_args) expect(Chef::HTTP::Simple).to receive(:new).once.with(*expected_http_args).and_return(rest) fetcher.fetch expect(cache_control_data.etag).to be_nil expect(cache_control_data.mtime).to be_nil expect(cache_control_data.checksum).to eq(fetched_content_checksum) end end end end end chef-12.3.0/spec/unit/provider/remote_file/ftp_spec.rb0000644000004100000410000001650412520074675022735 0ustar www-datawww-data# # Author:: Jesse Campbell () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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::RemoteFile::FTP do let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } let(:new_resource) do r = Chef::Resource::RemoteFile.new("remote file ftp backend test (new resource)") r.ftp_active_mode(false) r.path(resource_path) r end let(:current_resource) do Chef::Resource::RemoteFile.new("remote file ftp backend test (current resource)'") end let(:ftp) do ftp = double(Net::FTP, { }) allow(ftp).to receive(:connect) allow(ftp).to receive(:login) allow(ftp).to receive(:voidcmd) allow(ftp).to receive(:mtime).and_return(Time.now) allow(ftp).to receive(:getbinaryfile) allow(ftp).to receive(:close) allow(ftp).to receive(:passive=) ftp end let(:tempfile_path) { "/tmp/somedir/remote-file-ftp-backend-spec-test" } let(:tempfile) do t = StringIO.new allow(t).to receive(:path).and_return(tempfile_path) t end let(:uri) { URI.parse("ftp://opscode.com/seattle.txt") } before(:each) do allow(Net::FTP).to receive(:new).with(no_args).and_return(ftp) allow(Tempfile).to receive(:new).and_return(tempfile) end describe "when first created" do it "throws an argument exception when no path is given" do uri.path = "" expect { Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) }.to raise_error(ArgumentError) end it "throws an argument exception when only a / is given" do uri.path = "/" expect { Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) }.to raise_error(ArgumentError) end it "throws an argument exception when no filename is given" do uri.path = "/the/whole/path/" expect { Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) }.to raise_error(ArgumentError) end it "throws an argument exception when the typecode is invalid" do uri.typecode = "d" expect { Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) }.to raise_error(ArgumentError) end it "does not use passive mode when new_resource sets ftp_active_mode to true" do new_resource.ftp_active_mode(true) fetcher = Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) expect(fetcher.use_passive_mode?).to be_falsey end it "uses passive mode when new_resource sets ftp_active_mode to false" do new_resource.ftp_active_mode(false) fetcher = Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) expect(fetcher.use_passive_mode?).to be_truthy end end describe "when fetching the object" do let(:cache_control_data) { Chef::Provider::RemoteFile::CacheControlData.new(uri) } let(:current_resource_checksum) { "e2a8938cc31754f6c067b35aab1d0d4864272e9bf8504536ef3e79ebf8432305" } subject(:fetcher) { Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) } before do current_resource.checksum(current_resource_checksum) #Chef::Provider::RemoteFile::CacheControlData.should_receive(:load_and_validate).with(uri, current_resource_checksum).and_return(cache_control_data) end it "should connect to the host from the uri on the default port 21" do expect(ftp).to receive(:connect).with("opscode.com", 21) fetcher.fetch end it "should set passive true when ftp_active_mode is false" do new_resource.ftp_active_mode(false) expect(ftp).to receive(:passive=).with(true) fetcher.fetch end it "should set passive false when ftp_active_mode is false" do new_resource.ftp_active_mode(true) expect(ftp).to receive(:passive=).with(false) fetcher.fetch end it "should use anonymous ftp when no userinfo is provided" do expect(ftp).to receive(:login).with("anonymous", nil) fetcher.fetch end context "and the URI specifies an alternate port" do let(:uri) { URI.parse("ftp://opscode.com:8021/seattle.txt") } it "should connect on an alternate port when one is provided" do uri = URI.parse("ftp://opscode.com:8021/seattle.txt") expect(ftp).to receive(:connect).with("opscode.com", 8021) fetcher.fetch end end context "and the URI contains a username and password" do let(:uri) { URI.parse("ftp://the_user:the_password@opscode.com/seattle.txt") } it "should use authenticated ftp when userinfo is provided" do expect(ftp).to receive(:login).with("the_user", "the_password") fetcher.fetch end end context "and the uri sets the typecode to ascii" do let(:uri) { URI.parse("ftp://the_user:the_password@opscode.com/seattle.txt;type=a") } it "fetches the file with ascii typecode set" do expect(ftp).to receive(:voidcmd).with("TYPE A").once fetcher.fetch end end context "and the uri sets the typecode to image" do let(:uri) { URI.parse("ftp://the_user:the_password@opscode.com/seattle.txt;type=i") } it "should accept image for the typecode" do expect(ftp).to receive(:voidcmd).with("TYPE I").once fetcher.fetch end end context "and the uri specifies a nested path" do let(:uri) { URI.parse("ftp://opscode.com/the/whole/path/seattle.txt") } it "should fetch the file from the correct path" do expect(ftp).to receive(:voidcmd).with("CWD the").once expect(ftp).to receive(:voidcmd).with("CWD whole").once expect(ftp).to receive(:voidcmd).with("CWD path").once expect(ftp).to receive(:getbinaryfile).with("seattle.txt", tempfile.path) fetcher.fetch end end context "when not using last modified based conditional fetching" do before do new_resource.use_last_modified(false) end it "should return a tempfile in the result" do result = fetcher.fetch expect(result).to equal(tempfile) end end context "and proxying is enabled" do before do Chef::Config[:ftp_proxy] = "socks5://socks.example.com:5000" Chef::Config[:ftp_proxy_user] = "bill" Chef::Config[:ftp_proxy_pass] = "ted" end it "fetches the file via the proxy" do current_socks_server = ENV["SOCKS_SERVER"] expect(ENV).to receive(:[]=).with("SOCKS_SERVER", "socks5://bill:ted@socks.example.com:5000").ordered expect(ENV).to receive(:[]=).with("SOCKS_SERVER", current_socks_server).ordered result = fetcher.fetch expect(result).to equal(tempfile) end end end end chef-12.3.0/spec/unit/provider/user_spec.rb0000644000004100000410000004054112520074675020626 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' EtcPwnamIsh = Struct.new(:name, :passwd, :uid, :gid, :gecos, :dir, :shell, :change, :uclass, :expire) EtcGrnamIsh = Struct.new(:name, :passwd, :gid, :mem) describe Chef::Provider::User do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::User.new("adam") @new_resource.comment "Adam Jacob" @new_resource.uid 1000 @new_resource.gid 1000 @new_resource.home "/home/adam" @new_resource.shell "/usr/bin/zsh" @current_resource = Chef::Resource::User.new("adam") @current_resource.comment "Adam Jacob" @current_resource.uid 1000 @current_resource.gid 1000 @current_resource.home "/home/adam" @current_resource.shell "/usr/bin/zsh" @provider = Chef::Provider::User.new(@new_resource, @run_context) @provider.current_resource = @current_resource end describe "when first created" do it "assume the user exists by default" do expect(@provider.user_exists).to eql(true) end it "does not know the locked state" do expect(@provider.locked).to eql(nil) end end describe "executing load_current_resource" do before(:each) do @node = Chef::Node.new #@new_resource = double("Chef::Resource::User", # :null_object => true, # :username => "adam", # :comment => "Adam Jacob", # :uid => 1000, # :gid => 1000, # :home => "/home/adam", # :shell => "/usr/bin/zsh", # :password => nil, # :updated => nil #) allow(Chef::Resource::User).to receive(:new).and_return(@current_resource) @pw_user = EtcPwnamIsh.new @pw_user.name = "adam" @pw_user.gid = 1000 @pw_user.uid = 1000 @pw_user.gecos = "Adam Jacob" @pw_user.dir = "/home/adam" @pw_user.shell = "/usr/bin/zsh" @pw_user.passwd = "*" allow(Etc).to receive(:getpwnam).and_return(@pw_user) end it "should create a current resource with the same name as the new resource" do @provider.load_current_resource expect(@provider.current_resource.name).to eq('adam') end it "should set the username of the current resource to the username of the new resource" do @provider.load_current_resource expect(@current_resource.username).to eq(@new_resource.username) end it "should change the encoding of gecos to the encoding of the new resource" do @pw_user.gecos.force_encoding('ASCII-8BIT') @provider.load_current_resource expect(@provider.current_resource.comment.encoding).to eq(@new_resource.comment.encoding) end it "should look up the user in /etc/passwd with getpwnam" do expect(Etc).to receive(:getpwnam).with(@new_resource.username).and_return(@pw_user) @provider.load_current_resource end it "should set user_exists to false if the user is not found with getpwnam" do expect(Etc).to receive(:getpwnam).and_raise(ArgumentError) @provider.load_current_resource expect(@provider.user_exists).to eql(false) end # The mapping between the Chef::Resource::User and Getpwnam struct user_attrib_map = { :uid => :uid, :gid => :gid, :comment => :gecos, :home => :dir, :shell => :shell } user_attrib_map.each do |user_attrib, getpwnam_attrib| it "should set the current resources #{user_attrib} based on getpwnam #{getpwnam_attrib}" do expect(@current_resource).to receive(user_attrib).with(@pw_user.send(getpwnam_attrib)) @provider.load_current_resource end end it "should attempt to convert the group gid if one has been supplied" do expect(@provider).to receive(:convert_group_name) @provider.load_current_resource end it "shouldn't try and convert the group gid if none has been supplied" do allow(@new_resource).to receive(:gid).and_return(nil) expect(@provider).not_to receive(:convert_group_name) @provider.load_current_resource end it "should return the current resource" do expect(@provider.load_current_resource).to eql(@current_resource) end describe "and running assertions" do def self.shadow_lib_unavail? begin require 'rubygems' require 'shadow' rescue LoadError => e pending "ruby-shadow gem not installed for dynamic load test" true else false end end before (:each) do user = @pw_user.dup user.name = "root" user.passwd = "x" @new_resource.password "some new password" allow(Etc).to receive(:getpwnam).and_return(user) end unless shadow_lib_unavail? context "and we have the ruby-shadow gem" do pending "and we are not root (rerun this again as root)", :requires_unprivileged_user => true context "and we are root", :requires_root => true do it "should pass assertions when ruby-shadow can be loaded" do @provider.action = 'create' original_method = @provider.method(:require) expect(@provider).to receive(:require) { |*args| original_method.call(*args) } passwd_info = Struct::PasswdEntry.new(:sp_namp => "adm ", :sp_pwdp => "$1$T0N0Q.lc$nyG6pFI3Dpqa5cxUz/57j0", :sp_lstchg => 14861, :sp_min => 0, :sp_max => 99999, :sp_warn => 7, :sp_inact => -1, :sp_expire => -1, :sp_flag => -1) expect(Shadow::Passwd).to receive(:getspnam).with("adam").and_return(passwd_info) @provider.load_current_resource @provider.define_resource_requirements @provider.process_resource_requirements end end end end it "should fail assertions when ruby-shadow cannot be loaded" do expect(@provider).to receive(:require).with("shadow") { raise LoadError } @provider.load_current_resource @provider.define_resource_requirements expect {@provider.process_resource_requirements}.to raise_error Chef::Exceptions::MissingLibrary end end end describe "compare_user" do let(:mapping) { { 'username' => ["adam", "Adam"], 'comment' => ["Adam Jacob", "adam jacob"], 'uid' => [1000, 1001], 'gid' => [1000, 1001], 'home' => ["/home/adam", "/Users/adam"], 'shell'=> ["/usr/bin/zsh", "/bin/bash"], 'password'=> ["abcd","12345"] } } %w{uid gid comment home shell password}.each do |attribute| it "should return true if #{attribute} doesn't match" do @new_resource.send(attribute, mapping[attribute][0]) @current_resource.send(attribute, mapping[attribute][1]) expect(@provider.compare_user).to eql(true) end end %w{uid gid}.each do |attribute| it "should return false if string #{attribute} matches fixnum" do @new_resource.send(attribute, "100") @current_resource.send(attribute, 100) expect(@provider.compare_user).to eql(false) end end it "should return false if the objects are identical" do expect(@provider.compare_user).to eql(false) end end describe "action_create" do before(:each) do allow(@provider).to receive(:load_current_resource) # @current_resource = double("Chef::Resource::User", # :null_object => true, # :username => "adam", # :comment => "Adam Jacob", # :uid => 1000, # :gid => 1000, # :home => "/home/adam", # :shell => "/usr/bin/zsh", # :password => nil, # :updated => nil # ) # @provider = Chef::Provider::User.new(@node, @new_resource) # @provider.current_resource = @current_resource # @provider.user_exists = false # @provider.stub(:create_user).and_return(true) # @provider.stub(:manage_user).and_return(true) end it "should call create_user if the user does not exist" do @provider.user_exists = false expect(@provider).to receive(:create_user).and_return(true) @provider.action_create @provider.set_updated_status expect(@new_resource).to be_updated end it "should call manage_user if the user exists and has mismatched attributes" do @provider.user_exists = true allow(@provider).to receive(:compare_user).and_return(true) expect(@provider).to receive(:manage_user).and_return(true) @provider.action_create end it "should set the new_resources updated flag when it creates the user if we call manage_user" do @provider.user_exists = true allow(@provider).to receive(:compare_user).and_return(true) allow(@provider).to receive(:manage_user).and_return(true) @provider.action_create @provider.set_updated_status expect(@new_resource).to be_updated end end describe "action_remove" do before(:each) do allow(@provider).to receive(:load_current_resource) end it "should not call remove_user if the user does not exist" do @provider.user_exists = false expect(@provider).not_to receive(:remove_user) @provider.action_remove end it "should call remove_user if the user exists" do @provider.user_exists = true expect(@provider).to receive(:remove_user) @provider.action_remove end it "should set the new_resources updated flag to true if the user is removed" do @provider.user_exists = true expect(@provider).to receive(:remove_user) @provider.action_remove @provider.set_updated_status expect(@new_resource).to be_updated end end describe "action_manage" do before(:each) do allow(@provider).to receive(:load_current_resource) # @node = Chef::Node.new # @new_resource = double("Chef::Resource::User", # :null_object => true # ) # @current_resource = double("Chef::Resource::User", # :null_object => true # ) # @provider = Chef::Provider::User.new(@node, @new_resource) # @provider.current_resource = @current_resource # @provider.user_exists = true # @provider.stub(:manage_user).and_return(true) end it "should run manage_user if the user exists and has mismatched attributes" do expect(@provider).to receive(:compare_user).and_return(true) expect(@provider).to receive(:manage_user).and_return(true) @provider.action_manage end it "should set the new resources updated flag to true if manage_user is called" do allow(@provider).to receive(:compare_user).and_return(true) allow(@provider).to receive(:manage_user).and_return(true) @provider.action_manage @provider.set_updated_status expect(@new_resource).to be_updated end it "should not run manage_user if the user does not exist" do @provider.user_exists = false expect(@provider).not_to receive(:manage_user) @provider.action_manage end it "should not run manage_user if the user exists but has no differing attributes" do expect(@provider).to receive(:compare_user).and_return(false) expect(@provider).not_to receive(:manage_user) @provider.action_manage end end describe "action_modify" do before(:each) do allow(@provider).to receive(:load_current_resource) # @node = Chef::Node.new # @new_resource = double("Chef::Resource::User", # :null_object => true # ) # @current_resource = double("Chef::Resource::User", # :null_object => true # ) # @provider = Chef::Provider::User.new(@node, @new_resource) # @provider.current_resource = @current_resource # @provider.user_exists = true # @provider.stub(:manage_user).and_return(true) end it "should run manage_user if the user exists and has mismatched attributes" do expect(@provider).to receive(:compare_user).and_return(true) expect(@provider).to receive(:manage_user).and_return(true) @provider.action_modify end it "should set the new resources updated flag to true if manage_user is called" do allow(@provider).to receive(:compare_user).and_return(true) allow(@provider).to receive(:manage_user).and_return(true) @provider.action_modify @provider.set_updated_status expect(@new_resource).to be_updated end it "should not run manage_user if the user exists but has no differing attributes" do expect(@provider).to receive(:compare_user).and_return(false) expect(@provider).not_to receive(:manage_user) @provider.action_modify end it "should raise a Chef::Exceptions::User if the user doesn't exist" do @provider.user_exists = false expect { @provider.action = :modify; @provider.run_action }.to raise_error(Chef::Exceptions::User) end end describe "action_lock" do before(:each) do allow(@provider).to receive(:load_current_resource) end it "should lock the user if it exists and is unlocked" do allow(@provider).to receive(:check_lock).and_return(false) expect(@provider).to receive(:lock_user).and_return(true) @provider.action_lock end it "should set the new resources updated flag to true if lock_user is called" do allow(@provider).to receive(:check_lock).and_return(false) expect(@provider).to receive(:lock_user) @provider.action_lock @provider.set_updated_status expect(@new_resource).to be_updated end it "should raise a Chef::Exceptions::User if we try and lock a user that does not exist" do @provider.user_exists = false @provider.action = :lock expect { @provider.run_action }.to raise_error(Chef::Exceptions::User) end end describe "action_unlock" do before(:each) do allow(@provider).to receive(:load_current_resource) # @node = Chef::Node.new # @new_resource = double("Chef::Resource::User", # :null_object => true # ) # @current_resource = double("Chef::Resource::User", # :null_object => true # ) # @provider = Chef::Provider::User.new(@node, @new_resource) # @provider.current_resource = @current_resource # @provider.user_exists = true # @provider.stub(:check_lock).and_return(true) # @provider.stub(:unlock_user).and_return(true) end it "should unlock the user if it exists and is locked" do allow(@provider).to receive(:check_lock).and_return(true) expect(@provider).to receive(:unlock_user).and_return(true) @provider.action_unlock @provider.set_updated_status expect(@new_resource).to be_updated end it "should raise a Chef::Exceptions::User if we try and unlock a user that does not exist" do @provider.user_exists = false @provider.action = :unlock expect { @provider.run_action }.to raise_error(Chef::Exceptions::User) end end describe "convert_group_name" do before do @new_resource.gid('999') @group = EtcGrnamIsh.new('wheel', '*', 999, []) end it "should lookup the group name locally" do expect(Etc).to receive(:getgrnam).with("999").and_return(@group) expect(@provider.convert_group_name).to eq(999) end it "should raise an error if we can't translate the group name during resource assertions" do expect(Etc).to receive(:getgrnam).and_raise(ArgumentError) @provider.define_resource_requirements @provider.convert_group_name expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::User) end it "should set the new resources gid to the integerized version if available" do expect(Etc).to receive(:getgrnam).with("999").and_return(@group) @provider.convert_group_name expect(@new_resource.gid).to eq(999) end end end chef-12.3.0/spec/unit/provider/directory_spec.rb0000644000004100000410000001703712520074675021660 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'ostruct' require 'spec_helper' require 'tmpdir' describe Chef::Provider::Directory do before(:each) do @new_resource = Chef::Resource::Directory.new(Dir.tmpdir) if !windows? @new_resource.owner(500) @new_resource.group(500) @new_resource.mode(0644) end @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @directory = Chef::Provider::Directory.new(@new_resource, @run_context) end describe "scanning file security metadata on windows" do before do end it "describes the directory's access rights" do skip end end describe "scanning file security metadata on unix" do before do allow(Chef::Platform).to receive(:windows?).and_return(false) end let(:mock_stat) do cstats = double("stats") allow(cstats).to receive(:uid).and_return(500) allow(cstats).to receive(:gid).and_return(500) allow(cstats).to receive(:mode).and_return(0755) cstats end it "describes the access mode as a String of octal integers" do allow(File).to receive(:exists?).and_return(true) expect(File).to receive(:stat).and_return(mock_stat) @directory.load_current_resource expect(@directory.current_resource.mode).to eq("0755") end context "when user and group are specified with UID/GID" do it "describes the current owner and group as UID and GID" do allow(File).to receive(:exists?).and_return(true) expect(File).to receive(:stat).and_return(mock_stat) @directory.load_current_resource expect(@directory.current_resource.path).to eql(@new_resource.path) expect(@directory.current_resource.owner).to eql(500) expect(@directory.current_resource.group).to eql(500) end end context "when user/group are specified with user/group names" do end end # Unix only for now. While file security attribute reporting for windows is # disabled, unix and windows differ in the number of exists? calls that are # made by the provider. it "should create a new directory on create, setting updated to true", :unix_only do @new_resource.path "/tmp/foo" expect(File).to receive(:exists?).at_least(:once).and_return(false) expect(File).to receive(:directory?).with("/tmp").and_return(true) expect(Dir).to receive(:mkdir).with(@new_resource.path).once.and_return(true) expect(@directory).to receive(:do_acl_changes) allow(@directory).to receive(:do_selinux) @directory.run_action(:create) expect(@directory.new_resource).to be_updated end it "should raise an exception if the parent directory does not exist and recursive is false" do @new_resource.path "/tmp/some/dir" @new_resource.recursive false expect { @directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist) end # Unix only for now. While file security attribute reporting for windows is # disabled, unix and windows differ in the number of exists? calls that are # made by the provider. it "should create a new directory when parent directory does not exist if recursive is true and permissions are correct", :unix_only do @new_resource.path "/path/to/dir" @new_resource.recursive true expect(File).to receive(:exists?).with(@new_resource.path).ordered.and_return(false) expect(File).to receive(:exists?).with('/path/to').ordered.and_return(false) expect(File).to receive(:exists?).with('/path').ordered.and_return(true) expect(Chef::FileAccessControl).to receive(:writable?).with('/path').ordered.and_return(true) expect(File).to receive(:exists?).with(@new_resource.path).ordered.and_return(false) expect(FileUtils).to receive(:mkdir_p).with(@new_resource.path).and_return(true) expect(@directory).to receive(:do_acl_changes) allow(@directory).to receive(:do_selinux) @directory.run_action(:create) expect(@new_resource).to be_updated end it "should raise an error when creating a directory when parent directory is a file" do expect(File).to receive(:directory?).and_return(false) expect(Dir).not_to receive(:mkdir).with(@new_resource.path) expect { @directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist) expect(@directory.new_resource).not_to be_updated end # Unix only for now. While file security attribute reporting for windows is # disabled, unix and windows differ in the number of exists? calls that are # made by the provider. it "should not create the directory if it already exists", :unix_only do stub_file_cstats @new_resource.path "/tmp/foo" expect(File).to receive(:directory?).at_least(:once).and_return(true) expect(Chef::FileAccessControl).to receive(:writable?).with("/tmp").and_return(true) expect(File).to receive(:exists?).at_least(:once).and_return(true) expect(Dir).not_to receive(:mkdir).with(@new_resource.path) expect(@directory).to receive(:do_acl_changes) @directory.run_action(:create) end it "should delete the directory if it exists, and is writable with action_delete" do expect(File).to receive(:directory?).and_return(true) expect(Chef::FileAccessControl).to receive(:writable?).once.and_return(true) expect(Dir).to receive(:delete).with(@new_resource.path).once.and_return(true) @directory.run_action(:delete) end it "should raise an exception if it cannot delete the directory due to bad permissions" do allow(File).to receive(:exists?).and_return(true) allow(Chef::FileAccessControl).to receive(:writable?).and_return(false) expect { @directory.run_action(:delete) }.to raise_error(RuntimeError) end it "should take no action when deleting a target directory that does not exist" do @new_resource.path "/an/invalid/path" allow(File).to receive(:exists?).and_return(false) expect(Dir).not_to receive(:delete).with(@new_resource.path) @directory.run_action(:delete) expect(@directory.new_resource).not_to be_updated end it "should raise an exception when deleting a directory when target directory is a file" do stub_file_cstats @new_resource.path "/an/invalid/path" allow(File).to receive(:exists?).and_return(true) expect(File).to receive(:directory?).and_return(false) expect(Dir).not_to receive(:delete).with(@new_resource.path) expect { @directory.run_action(:delete) }.to raise_error(RuntimeError) expect(@directory.new_resource).not_to be_updated end def stub_file_cstats cstats = double("stats") allow(cstats).to receive(:uid).and_return(500) allow(cstats).to receive(:gid).and_return(500) allow(cstats).to receive(:mode).and_return(0755) # File.stat is called in: # - Chef::Provider::File.load_current_resource_attrs # - Chef::ScanAccessControl via Chef::Provider::File.setup_acl allow(File).to receive(:stat).and_return(cstats) end end chef-12.3.0/spec/unit/provider/deploy/0000755000004100000410000000000012520074675017601 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/deploy/timestamped_spec.rb0000644000004100000410000000267712520074675023470 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Deploy::Timestamped do before do @release_time = Time.utc( 2004, 8, 15, 16, 23, 42) allow(Time).to receive(:now).and_return(@release_time) @expected_release_dir = "/my/deploy/dir/releases/20040815162342" @resource = Chef::Resource::Deploy.new("/my/deploy/dir") @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @timestamped_deploy = Chef::Provider::Deploy::Timestamped.new(@resource, @run_context) @runner = double("runnah") allow(Chef::Runner).to receive(:new).and_return(@runner) end it "gives a timestamp for release_slug" do expect(@timestamped_deploy.send(:release_slug)).to eq("20040815162342") end end chef-12.3.0/spec/unit/provider/deploy/revision_spec.rb0000644000004100000410000001051712520074675023002 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Deploy::Revision do before do allow(Chef::Platform).to receive(:windows?) { false } @temp_dir = Dir.mktmpdir Chef::Config[:file_cache_path] = @temp_dir @resource = Chef::Resource::Deploy.new("/my/deploy/dir") @resource.revision("8a3195bf3efa246f743c5dfa83683201880f935c") @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @provider = Chef::Provider::Deploy::Revision.new(@resource, @run_context) @provider.load_current_resource @runner = double("runnah") allow(Chef::Runner).to receive(:new).and_return(@runner) @expected_release_dir = "/my/deploy/dir/releases/8a3195bf3efa246f743c5dfa83683201880f935c" end after do # Make sure we don't keep any state in our tests FileUtils.rm_rf @temp_dir if File.directory?( @temp_dir ) end it "uses the resolved revision from the SCM as the release slug" do allow(@provider.scm_provider).to receive(:revision_slug).and_return("uglySlugly") expect(@provider.send(:release_slug)).to eq("uglySlugly") end it "deploys to a dir named after the revision" do expect(@provider.release_path).to eq(@expected_release_dir) end it "stores the release dir in the file cache in the cleanup step" do allow(FileUtils).to receive(:mkdir_p) allow(FileUtils).to receive(:cp_r) @provider.cleanup! allow(@provider).to receive(:release_slug).and_return("73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2") @provider.load_current_resource @provider.cleanup! second_release = "/my/deploy/dir/releases/73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2" expect(@provider.all_releases).to eq([@expected_release_dir,second_release]) end it "removes a release from the file cache when it's used again in another release and append it to the end" do allow(FileUtils).to receive(:mkdir_p) allow(FileUtils).to receive(:cp_r) @provider.cleanup! allow(@provider).to receive(:release_slug).and_return("73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2") @provider.load_current_resource @provider.cleanup! second_release = "/my/deploy/dir/releases/73219b87e977d9c7ba1aa57e9ad1d88fa91a0ec2" expect(@provider.all_releases).to eq([@expected_release_dir,second_release]) @provider.cleanup! allow(@provider).to receive(:release_slug).and_return("8a3195bf3efa246f743c5dfa83683201880f935c") @provider.load_current_resource @provider.cleanup! expect(@provider.all_releases).to eq([second_release, @expected_release_dir]) end it "removes a release from the file cache when it's deleted by :cleanup!" do release_paths = %w{first second third fourth fifth}.map do |release_name| "/my/deploy/dir/releases/#{release_name}" end release_paths.each do |release_path| @provider.send(:release_created, release_path) end expect(@provider.all_releases).to eq(release_paths) allow(FileUtils).to receive(:rm_rf) @provider.cleanup! expected_release_paths = (%w{second third fourth fifth} << @resource.revision).map do |release_name| "/my/deploy/dir/releases/#{release_name}" end expect(@provider.all_releases).to eq(expected_release_paths) end it "regenerates the file cache if it's not available" do oldest = "/my/deploy/dir/releases/oldest" latest = "/my/deploy/dir/releases/latest" expect(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return([latest, oldest]) expect(::File).to receive(:ctime).with(oldest).and_return(Time.now - 10) expect(::File).to receive(:ctime).with(latest).and_return(Time.now - 1) expect(@provider.all_releases).to eq([oldest, latest]) end end chef-12.3.0/spec/unit/provider/remote_directory_spec.rb0000644000004100000410000002271312520074675023230 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 Daniel DeLeo # 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 'digest/md5' require 'tmpdir' require 'chef/mixin/file_class' class Chef::CFCCheck include Chef::Mixin::FileClass end describe Chef::Provider::RemoteDirectory do before do allow_any_instance_of(Chef::FileAccessControl).to receive(:set_all) @resource = Chef::Resource::RemoteDirectory.new(File.join(Dir.tmpdir, "tafty")) # in CHEF_SPEC_DATA/cookbooks/openldap/files/default/remotedir @resource.source "remotedir" @resource.cookbook('openldap') @cookbook_repo = ::File.expand_path(::File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) cl.load_cookbooks @cookbook_collection = Chef::CookbookCollection.new(cl) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @provider = Chef::Provider::RemoteDirectory.new(@resource, @run_context) @provider.current_resource = @resource.clone end describe "when the contents of the directory changed on the first run and not on the second run" do before do @resource_second_run = @resource.clone @provider_second_run = Chef::Provider::RemoteDirectory.new(@resource_second_run, @run_context) @provider.run_action(:create) @provider_second_run.run_action(:create) end it "identifies that the state has changed the after first run" do @provider_second_run.new_resource.updated_by_last_action? == true end it "identifies that the state has not changed after the second run" do @provider_second_run.new_resource.updated_by_last_action? == false end end describe "when access control is configured on the resource" do before do @resource.mode "0750" @resource.group "wheel" @resource.owner "root" @resource.files_mode "0640" @resource.files_group "staff" @resource.files_owner "toor" @resource.files_backup 23 @resource.source "remotedir_root" end it "configures access control on intermediate directorys" do directory_resource = @provider.send(:resource_for_directory, File.join(Dir.tmpdir, "intermediate_dir")) expect(directory_resource.path).to eq(File.join(Dir.tmpdir, "intermediate_dir")) expect(directory_resource.mode).to eq("0750") expect(directory_resource.group).to eq("wheel") expect(directory_resource.owner).to eq("root") expect(directory_resource.recursive).to be_truthy end it "configures access control on files in the directory" do @resource.cookbook "berlin_style_tasty_cupcakes" cookbook_file = @provider.send(:cookbook_file_resource, "/target/destination/path.txt", "relative/source/path.txt") expect(cookbook_file.cookbook_name).to eq("berlin_style_tasty_cupcakes") expect(cookbook_file.source).to eq("remotedir_root/relative/source/path.txt") expect(cookbook_file.mode).to eq("0640") expect(cookbook_file.group).to eq("staff") expect(cookbook_file.owner).to eq("toor") expect(cookbook_file.backup).to eq(23) end end describe "when creating the remote directory" do before do @node.automatic_attrs[:platform] = :just_testing @node.automatic_attrs[:platform_version] = :just_testing @destination_dir = Dir.mktmpdir << "/remote_directory_test" @resource.path(@destination_dir) end after {FileUtils.rm_rf(@destination_dir)} # CHEF-3552 it "creates the toplevel directory without error " do @resource.recursive(false) @provider.run_action(:create) expect(::File.exist?(@destination_dir)).to be_truthy end it "transfers the directory with all contents" do @provider.run_action(:create) expect(::File.exist?(@destination_dir + '/remote_dir_file1.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remote_dir_file2.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remotesubdir/remote_subdir_file2.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remotesubdir/.a_dotfile')).to be_truthy expect(::File.exist?(@destination_dir + '/.a_dotdir/.a_dotfile_in_a_dotdir')).to be_truthy end describe "only if it is missing" do it "should not overwrite existing files" do @resource.overwrite(true) @provider.run_action(:create) File.open(@destination_dir + '/remote_dir_file1.txt', 'a') {|f| f.puts "blah blah blah" } File.open(@destination_dir + '/remotesubdir/remote_subdir_file1.txt', 'a') {|f| f.puts "blah blah blah" } file1md5 = Digest::MD5.hexdigest(File.read(@destination_dir + '/remote_dir_file1.txt')) subdirfile1md5 = Digest::MD5.hexdigest(File.read(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')) @provider.run_action(:create_if_missing) expect(file1md5.eql?(Digest::MD5.hexdigest(File.read(@destination_dir + '/remote_dir_file1.txt')))).to be_truthy expect(subdirfile1md5.eql?(Digest::MD5.hexdigest(File.read(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')))).to be_truthy end end describe "with purging enabled" do before {@resource.purge(true)} it "removes existing files if purge is true" do @provider.run_action(:create) FileUtils.touch(@destination_dir + '/marked_for_death.txt') FileUtils.touch(@destination_dir + '/remotesubdir/marked_for_death_again.txt') @provider.run_action(:create) expect(::File.exist?(@destination_dir + '/remote_dir_file1.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remote_dir_file2.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/remotesubdir/remote_subdir_file2.txt')).to be_truthy expect(::File.exist?(@destination_dir + '/marked_for_death.txt')).to be_falsey expect(::File.exist?(@destination_dir + '/remotesubdir/marked_for_death_again.txt')).to be_falsey end it "removes files in subdirectories before files above" do @provider.run_action(:create) FileUtils.mkdir_p(@destination_dir + '/a/multiply/nested/directory/') FileUtils.touch(@destination_dir + '/a/foo.txt') FileUtils.touch(@destination_dir + '/a/multiply/bar.txt') FileUtils.touch(@destination_dir + '/a/multiply/nested/baz.txt') FileUtils.touch(@destination_dir + '/a/multiply/nested/directory/qux.txt') @provider.run_action(:create) expect(::File.exist?(@destination_dir + '/a/foo.txt')).to be_falsey expect(::File.exist?(@destination_dir + '/a/multiply/bar.txt')).to be_falsey expect(::File.exist?(@destination_dir + '/a/multiply/nested/baz.txt')).to be_falsey expect(::File.exist?(@destination_dir + '/a/multiply/nested/directory/qux.txt')).to be_falsey end it "removes directory symlinks properly", :not_supported_on_win2k3 do symlinked_dir_path = @destination_dir + '/symlinked_dir' @provider.action = :create @provider.run_action @fclass = Chef::CFCCheck.new Dir.mktmpdir do |tmp_dir| begin @fclass.file_class.symlink(tmp_dir.dup, symlinked_dir_path) expect(::File.exist?(symlinked_dir_path)).to be_truthy @provider.run_action expect(::File.exist?(symlinked_dir_path)).to be_falsey expect(::File.exist?(tmp_dir)).to be_truthy rescue Chef::Exceptions::Win32APIError => e pending "This must be run as an Administrator to create symlinks" end end end end describe "with overwrite disabled" do before {@resource.purge(false)} before {@resource.overwrite(false)} it "leaves modifications alone" do @provider.run_action(:create) ::File.open(@destination_dir + '/remote_dir_file1.txt', 'a') {|f| f.puts "blah blah blah" } ::File.open(@destination_dir + '/remotesubdir/remote_subdir_file1.txt', 'a') {|f| f.puts "blah blah blah" } file1md5 = Digest::MD5.hexdigest(::File.read(@destination_dir + '/remote_dir_file1.txt')) subdirfile1md5 = Digest::MD5.hexdigest(::File.read(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')) @provider.run_action(:create) expect(file1md5.eql?(Digest::MD5.hexdigest(::File.read(@destination_dir + '/remote_dir_file1.txt')))).to be_truthy expect(subdirfile1md5.eql?(Digest::MD5.hexdigest(::File.read(@destination_dir + '/remotesubdir/remote_subdir_file1.txt')))).to be_truthy end end end end chef-12.3.0/spec/unit/provider/env/0000755000004100000410000000000012520074675017075 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/env/windows_spec.rb0000644000004100000410000000640612520074675022134 0ustar www-datawww-data# # Author:: Sander van Harmelen # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Env::Windows, :windows_only do let(:node) { Chef::Node.new } let(:events) {Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } context 'when environment variable is not PATH' do let(:new_resource) { new_resource = Chef::Resource::Env.new("CHEF_WINDOWS_ENV_TEST") new_resource.value("foo") new_resource } let(:provider) { provider = Chef::Provider::Env::Windows.new(new_resource, run_context) allow(provider).to receive(:env_obj).and_return(double('null object').as_null_object) provider } describe "action_create" do before do ENV.delete('CHEF_WINDOWS_ENV_TEST') provider.key_exists = false end it "should update the ruby ENV object when it creates the key" do provider.action_create expect(ENV['CHEF_WINDOWS_ENV_TEST']).to eql('foo') end end describe "action_modify" do before do ENV['CHEF_WINDOWS_ENV_TEST'] = 'foo' end it "should update the ruby ENV object when it updates the value" do expect(provider).to receive(:requires_modify_or_create?).and_return(true) new_resource.value("foobar") provider.action_modify expect(ENV['CHEF_WINDOWS_ENV_TEST']).to eql('foobar') end describe "action_delete" do before do ENV['CHEF_WINDOWS_ENV_TEST'] = 'foo' end it "should update the ruby ENV object when it deletes the key" do provider.action_delete expect(ENV['CHEF_WINDOWS_ENV_TEST']).to eql(nil) end end end end context 'when environment is PATH' do describe "for PATH" do let(:system_root) {'%SystemRoot%'} let(:system_root_value) { 'D:\Windows' } let(:new_resource) { new_resource = Chef::Resource::Env.new('PATH') new_resource.value(system_root) new_resource } let(:provider) { provider = Chef::Provider::Env::Windows.new(new_resource, run_context) allow(provider).to receive(:env_obj).and_return(double('null object').as_null_object) provider } before do stub_const('ENV', {'PATH' => ''}) end it "replaces Windows system variables" do expect(provider).to receive(:requires_modify_or_create?).and_return(true) expect(provider).to receive(:expand_path).with(system_root).and_return(system_root_value) provider.action_modify expect(ENV['PATH']).to eql(system_root_value) end end end end chef-12.3.0/spec/unit/provider/user/0000755000004100000410000000000012520074675017263 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/user/useradd_spec.rb0000644000004100000410000000330212520074675022247 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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/provider/user/useradd' describe Chef::Provider::User::Useradd do subject(:provider) do p = described_class.new(@new_resource, @run_context) p.current_resource = @current_resource p end supported_useradd_options = { 'comment' => "-c", 'gid' => "-g", 'uid' => "-u", 'shell' => "-s", 'password' => "-p" } include_examples "a useradd-based user provider", supported_useradd_options describe "manage_user" do # CHEF-5247: Chef::Provider::User::Solaris subclasses Chef::Provider::User::Useradd, but does not use usermod to change passwords. # Thus, a call to Solaris#manage_user calls Solaris#manage_password and Useradd#manage_user, but the latter should be a no-op. it "should not run the command if universal_options is an empty array" do allow(provider).to receive(:universal_options).and_return([]) expect(provider).not_to receive(:shell_out!) provider.manage_user end end end chef-12.3.0/spec/unit/provider/user/windows_spec.rb0000644000004100000410000001354112520074675022320 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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' class Chef class Util class Windows class NetUser end end end end describe Chef::Provider::User::Windows do before(:each) do @node = Chef::Node.new @new_resource = Chef::Resource::User.new("monkey") @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @current_resource = Chef::Resource::User.new("monkey") @net_user = double("Chef::Util::Windows::NetUser") allow(Chef::Util::Windows::NetUser).to receive(:new).and_return(@net_user) @provider = Chef::Provider::User::Windows.new(@new_resource, @run_context) @provider.current_resource = @current_resource end it "creates a net_user object with the provided username" do @new_resource.username "not-monkey" expect(Chef::Util::Windows::NetUser).to receive(:new).with("not-monkey") @provider = Chef::Provider::User::Windows.new(@new_resource, @run_context) end describe "when comparing the user's current attributes to the desired attributes" do before do @new_resource.comment "Adam Jacob" @new_resource.uid 1000 @new_resource.gid 1000 @new_resource.home "/home/adam" @new_resource.shell "/usr/bin/zsh" @new_resource.password "abracadabra" @provider.current_resource = @new_resource.clone end describe "and the attributes match" do it "doesn't set the comment field to be updated" do expect(@provider.set_options).not_to have_key(:full_name) end it "doesn't set the home directory to be updated" do expect(@provider.set_options).not_to have_key(:home_dir) end it "doesn't set the group id to be updated" do expect(@provider.set_options).not_to have_key(:primary_group_id) end it "doesn't set the user id to be updated" do expect(@provider.set_options).not_to have_key(:user_id) end it "doesn't set the shell to be updated" do expect(@provider.set_options).not_to have_key(:script_path) end it "doesn't set the password to be updated" do expect(@provider.set_options).not_to have_key(:password) end end describe "and the attributes do not match" do before do @current_resource = Chef::Resource::User.new("adam") @current_resource.comment "Adam Jacob-foo" @current_resource.uid 1111 @current_resource.gid 1111 @current_resource.home "/home/adam-foo" @current_resource.shell "/usr/bin/tcsh" @current_resource.password "foobarbaz" @provider.current_resource = @current_resource end it "marks the full_name field to be updated" do expect(@provider.set_options[:full_name]).to eq("Adam Jacob") end it "marks the home_dir attribute to be updated" do expect(@provider.set_options[:home_dir]).to eq('/home/adam') end it "marks the primary_group_id attribute to be updated" do expect(@provider.set_options[:primary_group_id]).to eq(1000) end it "marks the user_id attribute to be updated" do expect(@provider.set_options[:user_id]).to eq(1000) end it "marks the script_path attribute to be updated" do expect(@provider.set_options[:script_path]).to eq('/usr/bin/zsh') end it "marks the password attribute to be updated" do expect(@provider.set_options[:password]).to eq('abracadabra') end end end describe "when creating the user" do it "should call @net_user.add with the return of set_options" do allow(@provider).to receive(:set_options).and_return(:name=> "monkey") expect(@net_user).to receive(:add).with(:name=> "monkey") @provider.create_user end end describe "manage_user" do before(:each) do allow(@provider).to receive(:set_options).and_return(:name=> "monkey") end it "should call @net_user.update with the return of set_options" do expect(@net_user).to receive(:update).with(:name=> "monkey") @provider.manage_user end end describe "when removing the user" do it "should call @net_user.delete" do expect(@net_user).to receive(:delete) @provider.remove_user end end describe "when checking if the user is locked" do before(:each) do @current_resource.password "abracadabra" end it "should return true if user is locked" do allow(@net_user).to receive(:check_enabled).and_return(true) expect(@provider.check_lock).to eql(true) end it "should return false if user is not locked" do allow(@net_user).to receive(:check_enabled).and_return(false) expect(@provider.check_lock).to eql(false) end end describe "locking the user" do it "should call @net_user.disable_account" do allow(@net_user).to receive(:check_enabled).and_return(true) expect(@net_user).to receive(:disable_account) @provider.lock_user end end describe "unlocking the user" do it "should call @net_user.enable_account" do allow(@net_user).to receive(:check_enabled).and_return(false) expect(@net_user).to receive(:enable_account) @provider.unlock_user end end end chef-12.3.0/spec/unit/provider/user/dscl_spec.rb0000644000004100000410000007716312520074675021565 0ustar www-datawww-data# # Author:: Dreamcat4 () # 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. # ShellCmdResult = Struct.new(:stdout, :stderr, :exitstatus) require 'spec_helper' require 'ostruct' require 'mixlib/shellout' describe Chef::Provider::User::Dscl do before do allow(Chef::Platform).to receive(:windows?) { false } end let(:node) { node = Chef::Node.new allow(node).to receive(:[]).with(:platform_version).and_return(mac_version) allow(node).to receive(:[]).with(:platform).and_return("mac_os_x") node } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) { r = Chef::Resource::User.new("toor") r.password(password) r.salt(salt) r.iterations(iterations) r } let(:provider) { Chef::Provider::User::Dscl.new(new_resource, run_context) } let(:mac_version) { "10.9.1" } let(:password) { nil } let(:salt) { nil } let(:iterations) { nil } let(:salted_sha512_password) { "0f543f021c63255e64e121a3585601b8ecfedf6d2\ 705ddac69e682a33db5dbcdb9b56a2520bc8fff63a\ 2ba6b7984c0737ff0b7949455071581f7affcd536d\ 402b6cdb097" } let(:salted_sha512_pbkdf2_password) { "c734b6e4787c3727bb35e29fdd92b97c\ 1de12df509577a045728255ec7c6c5f5\ c18efa05ed02b682ffa7ebc05119900e\ b1d4880833aa7a190afc13e2bf0936b8\ 20123e8c98f0f9bcac2a629d9163caac\ 9464a8c234f3919082400b4f939bb77b\ c5adbbac718b7eb99463a7b679571e0f\ 1c9fef2ef08d0b9e9c2bcf644eed2ffc" } let(:salted_sha512_pbkdf2_salt) { "2d942d8364a9ccf2b8e5cb7ed1ff58f78\ e29dbfee7f9db58859144d061fd0058" } let(:salted_sha512_pbkdf2_iterations) { 25000 } let(:vagrant_sha_512) { "6f75d7190441facc34291ebbea1fc756b242d4f\ e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30" } let(:vagrant_sha_512_pbkdf2) { "12601a90db17cbf\ 8ba4808e6382fb0d3b9d8a6c1a190477bf680ab21afb\ 6065467136e55cc208a6f74156e3daf20fb13369ef4b\ 7bafa047d80359fb46a48a4adccd548ebb33851b093\ 47cca84341a7f93a27147343f89fb843fb46c0017d2\ 64afa4976baacf941b915bd1ec1ca24c30b3e759e02\ 403e02f59fe7ff5938a7636c" } let(:vagrant_sha_512_pbkdf2_salt) { "ee954be472fdc60ddf89484781433993625f006af6ec810c08f49a7e413946a1" } let(:vagrant_sha_512_pbkdf2_iterations) { 34482 } describe "when shelling out to dscl" do it "should run dscl with the supplied cmd /Path args" do shell_return = ShellCmdResult.new('stdout', 'err', 0) expect(provider).to receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return) expect(provider.run_dscl("cmd /Path args")).to eq('stdout') end it "returns an empty string from delete commands" do shell_return = ShellCmdResult.new('out', 'err', 23) expect(provider).to receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return) expect(provider.run_dscl("delete /Path args")).to eq("") end it "should raise an exception for any other command" do shell_return = ShellCmdResult.new('out', 'err', 23) expect(provider).to receive(:shell_out).with('dscl . -cmd /Path arguments').and_return(shell_return) expect { provider.run_dscl("cmd /Path arguments") }.to raise_error(Chef::Exceptions::DsclCommandFailed) end it "raises an exception when dscl reports 'no such key'" do shell_return = ShellCmdResult.new("No such key: ", 'err', 23) expect(provider).to receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return) expect { provider.run_dscl("cmd /Path args") }.to raise_error(Chef::Exceptions::DsclCommandFailed) end it "raises an exception when dscl reports 'eDSRecordNotFound'" do shell_return = ShellCmdResult.new(" DS Error: -14136 (eDSRecordNotFound)", 'err', -14136) expect(provider).to receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return) expect { provider.run_dscl("cmd /Path args") }.to raise_error(Chef::Exceptions::DsclCommandFailed) end end describe "get_free_uid" do before do expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("\nwheel 200\nstaff 201\nbrahms 500\nchopin 501\n") end describe "when resource is configured as system" do before do new_resource.system(true) end it "should return the first unused uid number on or above 500" do expect(provider.get_free_uid).to eq(202) end end it "should return the first unused uid number on or above 200" do expect(provider.get_free_uid).to eq(502) end it "should raise an exception when the search limit is exhausted" do search_limit = 1 expect { provider.get_free_uid(search_limit) }.to raise_error(RuntimeError) end end describe "uid_used?" do it "should return false if not given any valid uid number" do expect(provider.uid_used?(nil)).to be_falsey end describe "when called with a user id" do before do expect(provider).to receive(:run_dscl).with("list /Users uid").and_return("\naj 500\n") end it "should return true for a used uid number" do expect(provider.uid_used?(500)).to be_truthy end it "should return false for an unused uid number" do expect(provider.uid_used?(501)).to be_falsey end end end describe "when determining the uid to set" do it "raises RequestedUIDUnavailable if the requested uid is already in use" do allow(provider).to receive(:uid_used?).and_return(true) expect(provider).to receive(:get_free_uid).and_return(501) expect { provider.dscl_set_uid }.to raise_error(Chef::Exceptions::RequestedUIDUnavailable) end it "finds a valid, unused uid when none is specified" do expect(provider).to receive(:run_dscl).with("list /Users uid").and_return('') expect(provider).to receive(:run_dscl).with("create /Users/toor UniqueID 501") expect(provider).to receive(:get_free_uid).and_return(501) provider.dscl_set_uid expect(new_resource.uid).to eq(501) end it "sets the uid specified in the resource" do new_resource.uid(1000) expect(provider).to receive(:run_dscl).with("create /Users/toor UniqueID 1000").and_return(true) expect(provider).to receive(:run_dscl).with("list /Users uid").and_return('') provider.dscl_set_uid end end describe "when modifying the home directory" do let(:current_resource) { new_resource.dup } before do new_resource.supports({ :manage_home => true }) new_resource.home('/Users/toor') provider.current_resource = current_resource end it "deletes the home directory when resource#home is nil" do new_resource.instance_variable_set(:@home, nil) expect(provider).to receive(:run_dscl).with("delete /Users/toor NFSHomeDirectory").and_return(true) provider.dscl_set_home end it "raises InvalidHomeDirectory when the resource's home directory doesn't look right" do new_resource.home('epic-fail') expect { provider.dscl_set_home }.to raise_error(Chef::Exceptions::InvalidHomeDirectory) end it "moves the users home to the new location if it exists and the target location is different" do new_resource.supports(:manage_home => true) current_home = CHEF_SPEC_DATA + '/old_home_dir' current_home_files = [current_home + '/my-dot-emacs', current_home + '/my-dot-vim'] current_resource.home(current_home) new_resource.gid(23) allow(::File).to receive(:exists?).with('/old/home/toor').and_return(true) allow(::File).to receive(:exists?).with('/Users/toor').and_return(true) expect(FileUtils).to receive(:mkdir_p).with('/Users/toor').and_return(true) expect(FileUtils).to receive(:rmdir).with(current_home) expect(::Dir).to receive(:glob).with("#{CHEF_SPEC_DATA}/old_home_dir/*",::File::FNM_DOTMATCH).and_return(current_home_files) expect(FileUtils).to receive(:mv).with(current_home_files, "/Users/toor", :force => true) expect(FileUtils).to receive(:chown_R).with('toor','23','/Users/toor') expect(provider).to receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'") provider.dscl_set_home end it "should raise an exception when the systems user template dir (skel) cannot be found" do allow(::File).to receive(:exists?).and_return(false,false,false) expect { provider.dscl_set_home }.to raise_error(Chef::Exceptions::User) end it "should run ditto to copy any missing files from skel to the new home dir" do expect(::File).to receive(:exists?).with("/System/Library/User\ Template/English.lproj").and_return(true) expect(FileUtils).to receive(:chown_R).with('toor', '', '/Users/toor') expect(provider).to receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'") provider.ditto_home end it "creates the user's NFSHomeDirectory and home directory" do expect(provider).to receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'").and_return(true) expect(provider).to receive(:ditto_home) provider.dscl_set_home end end describe "resource_requirements" do let(:dscl_exists) { true } let(:plutil_exists) { true } before do allow(::File).to receive(:exists?).with("/usr/bin/dscl").and_return(dscl_exists) allow(::File).to receive(:exists?).with("/usr/bin/plutil").and_return(plutil_exists) end def run_requirements provider.define_resource_requirements provider.action = :create provider.process_resource_requirements end describe "when dscl doesn't exist" do let(:dscl_exists) { false } it "should raise an error" do expect { run_requirements }.to raise_error end end describe "when plutil doesn't exist" do let(:plutil_exists) { false } it "should raise an error" do expect { run_requirements }.to raise_error end end describe "when on Mac 10.6" do let(:mac_version) { "10.6.5" } it "should raise an error" do expect { run_requirements }.to raise_error end end describe "when on Mac 10.7" do let(:mac_version) { "10.7.5" } describe "when password is SALTED-SHA512" do let(:password) { salted_sha512_password } it "should not raise an error" do expect { run_requirements }.not_to raise_error end end describe "when password is SALTED-SHA512-PBKDF2" do let(:password) { salted_sha512_pbkdf2_password } it "should raise an error" do expect { run_requirements }.to raise_error end end end [ "10.9", "10.10"].each do |version| describe "when on Mac #{version}" do let(:mac_version) { "#{version}.2" } describe "when password is SALTED-SHA512" do let(:password) { salted_sha512_password } it "should raise an error" do expect { run_requirements }.to raise_error end end describe "when password is SALTED-SHA512-PBKDF2" do let(:password) { salted_sha512_pbkdf2_password } describe "when salt and iteration is not set" do it "should raise an error" do expect { run_requirements }.to raise_error end end describe "when salt and iteration is set" do let(:salt) { salted_sha512_pbkdf2_salt } let(:iterations) { salted_sha512_pbkdf2_iterations } it "should not raise an error" do expect { run_requirements }.not_to raise_error end end end end end end describe "load_current_resource" do # set this to any of the user plist files under spec/data let(:user_plist_file) { nil } before do expect(provider).to receive(:shell_out).with("dscacheutil '-flushcache'") expect(provider).to receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do if user_plist_file.nil? ShellCmdResult.new('Can not find the file', 'Sorry!!', 1) else ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.plist.xml")), "", 0) end end if !user_plist_file.nil? expect(provider).to receive(:convert_binary_plist_to_xml).and_return(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.shadow.xml"))) end end describe "when user is not there" do it "shouldn't raise an error" do expect { provider.load_current_resource }.not_to raise_error end it "should set @user_exists" do provider.load_current_resource expect(provider.instance_variable_get(:@user_exists)).to be_falsey end it "should set username" do provider.load_current_resource expect(provider.current_resource.username).to eq("toor") end end describe "when user is there" do let(:password) { "something" } # Load password during load_current_resource describe "on 10.7" do let(:mac_version) { "10.7.5" } let(:user_plist_file) { "10.7" } it "collects the user data correctly" do provider.load_current_resource expect(provider.current_resource.comment).to eq("vagrant") expect(provider.current_resource.uid).to eq("501") expect(provider.current_resource.gid).to eq("80") expect(provider.current_resource.home).to eq("/Users/vagrant") expect(provider.current_resource.shell).to eq("/bin/bash") expect(provider.current_resource.password).to eq(vagrant_sha_512) end describe "when a plain password is set that is same" do let(:password) { "vagrant" } it "diverged_password? should report false" do provider.load_current_resource expect(provider.diverged_password?).to be_falsey end end describe "when a plain password is set that is different" do let(:password) { "not_vagrant" } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when iterations change" do let(:password) { vagrant_sha_512 } let(:iterations) { 12345 } it "diverged_password? should report false" do provider.load_current_resource expect(provider.diverged_password?).to be_falsey end end describe "when shadow hash changes" do let(:password) { salted_sha512_password } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when salt change" do let(:password) { vagrant_sha_512 } let(:salt) { "SOMETHINGRANDOM" } it "diverged_password? should report false" do provider.load_current_resource expect(provider.diverged_password?).to be_falsey end end end describe "on 10.8" do let(:mac_version) { "10.8.3" } let(:user_plist_file) { "10.8" } it "collects the user data correctly" do provider.load_current_resource expect(provider.current_resource.comment).to eq("vagrant") expect(provider.current_resource.uid).to eq("501") expect(provider.current_resource.gid).to eq("80") expect(provider.current_resource.home).to eq("/Users/vagrant") expect(provider.current_resource.shell).to eq("/bin/bash") expect(provider.current_resource.password).to eq("ea4c2d265d801ba0ec0dfccd\ 253dfc1de91cbe0806b4acc1ed7fe22aebcf6beb5344d0f442e590\ ffa04d679075da3afb119e41b72b5eaf08ee4aa54693722646d5\ 19ee04843deb8a3e977428d33f625e83887913e5c13b70035961\ 5e00ad7bc3e7a0c98afc3e19d1360272454f8d33a9214d2fbe8b\ e68d1f9821b26689312366") expect(provider.current_resource.salt).to eq("f994ef2f73b7c5594ebd1553300976b20733ce0e24d659783d87f3d81cbbb6a9") expect(provider.current_resource.iterations).to eq(39840) end end describe "on 10.7 upgraded to 10.8" do # In this scenario user password is still in 10.7 format let(:mac_version) { "10.8.3" } let(:user_plist_file) { "10.7-8" } it "collects the user data correctly" do provider.load_current_resource expect(provider.current_resource.comment).to eq("vagrant") expect(provider.current_resource.uid).to eq("501") expect(provider.current_resource.gid).to eq("80") expect(provider.current_resource.home).to eq("/Users/vagrant") expect(provider.current_resource.shell).to eq("/bin/bash") expect(provider.current_resource.password).to eq("6f75d7190441facc34291ebbea1fc756b242d4f\ e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30") end describe "when a plain text password is set" do it "reports password needs to be updated" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when a salted-sha512-pbkdf2 shadow is set" do let(:password) { salted_sha512_pbkdf2_password } let(:salt) { salted_sha512_pbkdf2_salt } let(:iterations) { salted_sha512_pbkdf2_iterations } it "reports password needs to be updated" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end end describe "on 10.9" do let(:mac_version) { "10.9.1" } let(:user_plist_file) { "10.9" } it "collects the user data correctly" do provider.load_current_resource expect(provider.current_resource.comment).to eq("vagrant") expect(provider.current_resource.uid).to eq("501") expect(provider.current_resource.gid).to eq("80") expect(provider.current_resource.home).to eq("/Users/vagrant") expect(provider.current_resource.shell).to eq("/bin/bash") expect(provider.current_resource.password).to eq(vagrant_sha_512_pbkdf2) expect(provider.current_resource.salt).to eq(vagrant_sha_512_pbkdf2_salt) expect(provider.current_resource.iterations).to eq(vagrant_sha_512_pbkdf2_iterations) end describe "when a plain password is set that is same" do let(:password) { "vagrant" } it "diverged_password? should report false" do provider.load_current_resource expect(provider.diverged_password?).to be_falsey end end describe "when a plain password is set that is different" do let(:password) { "not_vagrant" } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when iterations change" do let(:password) { vagrant_sha_512_pbkdf2 } let(:salt) { vagrant_sha_512_pbkdf2_salt } let(:iterations) { 12345 } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when shadow hash changes" do let(:password) { salted_sha512_pbkdf2_password } let(:salt) { vagrant_sha_512_pbkdf2_salt } let(:iterations) { vagrant_sha_512_pbkdf2_iterations } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when salt change" do let(:password) { vagrant_sha_512_pbkdf2 } let(:salt) { salted_sha512_pbkdf2_salt } let(:iterations) { vagrant_sha_512_pbkdf2_iterations } it "diverged_password? should report true" do provider.load_current_resource expect(provider.diverged_password?).to be_truthy end end describe "when salt isn't found" do it "diverged_password? should report true" do provider.load_current_resource provider.current_resource.salt(nil) expect(provider.diverged_password?).to be_truthy end end end end end describe "salted_sha512_pbkdf2?" do it "should return true when the string is a salted_sha512_pbkdf2 hash" do expect(provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password)).to be_truthy end it "should return false otherwise" do expect(provider.salted_sha512_pbkdf2?(salted_sha512_password)).to be_falsey expect(provider.salted_sha512_pbkdf2?("any other string")).to be_falsey end end describe "salted_sha512?" do it "should return true when the string is a salted_sha512_pbkdf2 hash" do expect(provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password)).to be_truthy end it "should return false otherwise" do expect(provider.salted_sha512?(salted_sha512_pbkdf2_password)).to be_falsey expect(provider.salted_sha512?("any other string")).to be_falsey end end describe "prepare_password_shadow_info" do describe "when on Mac 10.7" do let(:mac_version) { "10.7.1" } describe "when the password is plain text" do let(:password) { "vagrant" } it "password_shadow_info should have salted-sha-512 format" do shadow_info = provider.prepare_password_shadow_info expect(shadow_info).to have_key("SALTED-SHA512") info = shadow_info["SALTED-SHA512"].string.unpack('H*').first expect(provider.salted_sha512?(info)).to be_truthy end end describe "when the password is salted-sha-512" do let(:password) { vagrant_sha_512 } it "password_shadow_info should have salted-sha-512 format" do shadow_info = provider.prepare_password_shadow_info expect(shadow_info).to have_key("SALTED-SHA512") info = shadow_info["SALTED-SHA512"].string.unpack('H*').first expect(provider.salted_sha512?(info)).to be_truthy expect(info).to eq(vagrant_sha_512) end end end ["10.8", "10.9", "10.10"].each do |version| describe "when on Mac #{version}" do let(:mac_version) { "#{version}.1" } describe "when the password is plain text" do let(:password) { "vagrant" } it "password_shadow_info should have salted-sha-512 format" do shadow_info = provider.prepare_password_shadow_info expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations") info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy end end describe "when the password is salted-sha-512" do let(:password) { vagrant_sha_512_pbkdf2 } let(:iterations) { vagrant_sha_512_pbkdf2_iterations } let(:salt) { vagrant_sha_512_pbkdf2_salt } it "password_shadow_info should have salted-sha-512 format" do shadow_info = provider.prepare_password_shadow_info expect(shadow_info).to have_key("SALTED-SHA512-PBKDF2") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("entropy") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("salt") expect(shadow_info["SALTED-SHA512-PBKDF2"]).to have_key("iterations") info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first expect(provider.salted_sha512_pbkdf2?(info)).to be_truthy expect(info).to eq(vagrant_sha_512_pbkdf2) end end end end end describe "set_password" do before do new_resource.password("something") end it "should sleep and flush the dscl cache before saving the password" do expect(provider).to receive(:prepare_password_shadow_info).and_return({ }) mock_shellout = double("Mock::Shellout") allow(mock_shellout).to receive(:run_command) expect(Mixlib::ShellOut).to receive(:new).and_return(mock_shellout) expect(provider).to receive(:read_user_info) expect(provider).to receive(:dscl_set) expect(provider).to receive(:sleep).with(3) expect(provider).to receive(:save_user_info) provider.set_password end end describe "when the user does not yet exist and chef is creating it" do context "with a numeric gid" do before do new_resource.comment "#mockssuck" new_resource.gid 1001 end it "creates the user, comment field, sets uid, gid, configures the home directory, sets the shell, and sets the password" do expect(provider).to receive :dscl_create_user expect(provider).to receive :dscl_create_comment expect(provider).to receive :dscl_set_uid expect(provider).to receive :dscl_set_gid expect(provider).to receive :dscl_set_home expect(provider).to receive :dscl_set_shell expect(provider).to receive :set_password provider.create_user end it "creates the user and sets the comment field" do expect(provider).to receive(:run_dscl).with("create /Users/toor").and_return(true) provider.dscl_create_user end it "sets the comment field" do expect(provider).to receive(:run_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true) provider.dscl_create_comment end it "should run run_dscl with create /Users/user PrimaryGroupID to set the users primary group" do expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true) provider.dscl_set_gid end it "should run run_dscl with create /Users/user UserShell to set the users login shell" do expect(provider).to receive(:run_dscl).with("create /Users/toor UserShell '/usr/bin/false'").and_return(true) provider.dscl_set_shell end end context "with a non-numeric gid" do before do new_resource.comment "#mockssuck" new_resource.gid "newgroup" end it "should map the group name to a numeric ID when the group exists" do expect(provider).to receive(:run_dscl).with("read /Groups/newgroup PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n") expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").ordered.and_return(true) provider.dscl_set_gid end it "should raise an exception when the group does not exist" do shell_return = ShellCmdResult.new(" DS Error: -14136 (eDSRecordNotFound)", 'err', -14136) expect(provider).to receive(:shell_out).with('dscl . -read /Groups/newgroup PrimaryGroupID').and_return(shell_return) expect { provider.dscl_set_gid }.to raise_error(Chef::Exceptions::GroupIDNotFound) end end end describe "when the user exists and chef is managing it" do before do current_resource = new_resource.dup provider.current_resource = current_resource # These are all different from current_resource new_resource.username "mud" new_resource.uid 2342 new_resource.gid 2342 new_resource.home '/Users/death' new_resource.password 'goaway' end it "sets the user, comment field, uid, gid, moves the home directory, sets the shell, and sets the password" do expect(provider).to receive :dscl_create_user expect(provider).to receive :dscl_create_comment expect(provider).to receive :dscl_set_uid expect(provider).to receive :dscl_set_gid expect(provider).to receive :dscl_set_home expect(provider).to receive :dscl_set_shell expect(provider).to receive :set_password provider.create_user end end describe "when changing the gid" do before do current_resource = new_resource.dup provider.current_resource = current_resource # This is different from current_resource new_resource.gid 2342 end it "sets the gid" do expect(provider).to receive :dscl_set_gid provider.manage_user end end describe "when the user exists" do before do expect(provider).to receive(:shell_out).with("dscacheutil '-flushcache'") expect(provider).to receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/10.9.plist.xml")), "", 0) end provider.load_current_resource end describe "when Chef is removing the user" do it "removes the user from the groups and deletes home directory when the resource is configured to manage home" do new_resource.supports({ :manage_home => true }) expect(provider).to receive(:run_dscl).with("list /Groups").and_return("my_group\nyour_group\nreal_group\n") expect(provider).to receive(:run_dscl).with("read /Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group expect(provider).to receive(:run_dscl).with("read /Groups/your_group").and_return("GroupMembership: not_you") expect(provider).to receive(:run_dscl).with("read /Groups/real_group").and_return("GroupMembership: toor") expect(provider).to receive(:run_dscl).with("delete /Groups/real_group GroupMembership 'toor'") expect(provider).to receive(:run_dscl).with("delete /Users/toor") expect(FileUtils).to receive(:rm_rf).with("/Users/vagrant") provider.remove_user end end describe "when user is not locked" do it "determines the user as not locked" do expect(provider).not_to be_locked end end describe "when user is locked" do before do auth_authority = provider.instance_variable_get(:@authentication_authority) provider.instance_variable_set(:@authentication_authority, auth_authority + ";DisabledUser;") end it "determines the user as not locked" do expect(provider).to be_locked end it "can unlock the user" do expect(provider).to receive(:run_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;HASHLIST:'") provider.unlock_user end end end describe "when locking the user" do it "should run run_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do expect(provider).to receive(:run_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'") provider.lock_user end end end chef-12.3.0/spec/unit/provider/user/solaris_spec.rb0000644000004100000410000000523212520074675022300 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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::User::Solaris do subject(:provider) do p = described_class.new(@new_resource, @run_context) p.current_resource = @current_resource # Prevent the useradd-based provider tests from trying to write /etc/shadow allow(p).to receive(:write_shadow_file) p end supported_useradd_options = { 'comment' => "-c", 'gid' => "-g", 'uid' => "-u", 'shell' => "-s" } include_examples "a useradd-based user provider", supported_useradd_options describe "when we want to set a password" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::User.new("adam", @run_context) @current_resource = Chef::Resource::User.new("adam", @run_context) @new_resource.password "hocus-pocus" end it "should use its own shadow file writer to set the password" do expect(provider).to receive(:write_shadow_file) allow(provider).to receive(:shell_out!).and_return(true) provider.manage_user end it "should write out a modified version of the password file" do # Let this test run #write_shadow_file allow(provider).to receive(:write_shadow_file).and_call_original password_file = Tempfile.new("shadow") password_file.puts "adam:existingpassword:15441::::::" password_file.close provider.password_file = password_file.path allow(provider).to receive(:shell_out!).and_return(true) # may not be able to write to /etc for tests... temp_file = Tempfile.new("shadow") allow(Tempfile).to receive(:new).with("shadow", "/etc").and_return(temp_file) @new_resource.password "verysecurepassword" provider.manage_user expect(::File.open(password_file.path, "r").read).to match(/adam:verysecurepassword:/) password_file.unlink end end end chef-12.3.0/spec/unit/provider/user/pw_spec.rb0000644000004100000410000002215012520074675021250 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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' describe Chef::Provider::User::Pw do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::User.new("adam") @new_resource.comment "Adam Jacob" @new_resource.uid 1000 @new_resource.gid 1000 @new_resource.home "/home/adam" @new_resource.shell "/usr/bin/zsh" @new_resource.password "abracadabra" @new_resource.supports :manage_home => true @current_resource = Chef::Resource::User.new("adam") @current_resource.comment "Adam Jacob" @current_resource.uid 1000 @current_resource.gid 1000 @current_resource.home "/home/adam" @current_resource.shell "/usr/bin/zsh" @current_resource.password "abracadabra" @provider = Chef::Provider::User::Pw.new(@new_resource, @run_context) @provider.current_resource = @current_resource end describe "setting options to the pw command" do field_list = { 'comment' => "-c", 'home' => "-d", 'gid' => "-g", 'uid' => "-u", 'shell' => "-s" } field_list.each do |attribute, option| it "should check for differences in #{attribute} between the new and current resources" do expect(@current_resource).to receive(attribute) expect(@new_resource).to receive(attribute) @provider.set_options end it "should set the option for #{attribute} if the new resources #{attribute} is not null" do allow(@new_resource).to receive(attribute).and_return("hola") expect(@provider.set_options).to eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}' -m") end it "should set the option for #{attribute} if the new resources #{attribute} is not null, without homedir management" do allow(@new_resource).to receive(:supports).and_return({:manage_home => false}) allow(@new_resource).to receive(attribute).and_return("hola") expect(@provider.set_options).to eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}'") end end it "should combine all the possible options" do match_string = " adam" field_list.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option| allow(@new_resource).to receive(attribute).and_return("hola") match_string << " #{option} 'hola'" end match_string << " -m" expect(@provider.set_options).to eql(match_string) end end describe "create_user" do before(:each) do allow(@provider).to receive(:run_command).and_return(true) allow(@provider).to receive(:modify_password).and_return(true) end it "should run pw useradd with the return of set_options" do expect(@provider).to receive(:run_command).with({ :command => "pw useradd adam -m" }).and_return(true) @provider.create_user end it "should modify the password" do expect(@provider).to receive(:modify_password).and_return(true) @provider.create_user end end describe "manage_user" do before(:each) do allow(@provider).to receive(:run_command).and_return(true) allow(@provider).to receive(:modify_password).and_return(true) end it "should run pw usermod with the return of set_options" do expect(@provider).to receive(:run_command).with({ :command => "pw usermod adam -m" }).and_return(true) @provider.manage_user end it "should modify the password" do expect(@provider).to receive(:modify_password).and_return(true) @provider.create_user end end describe "remove_user" do it "should run pw userdel with the new resources user name" do @new_resource.supports :manage_home => false expect(@provider).to receive(:run_command).with({ :command => "pw userdel #{@new_resource.username}" }).and_return(true) @provider.remove_user end it "should run pw userdel with the new resources user name and -r if manage_home is true" do expect(@provider).to receive(:run_command).with({ :command => "pw userdel #{@new_resource.username} -r"}).and_return(true) @provider.remove_user end end describe "determining if the user is locked" do it "should return true if user is locked" do allow(@current_resource).to receive(:password).and_return("*LOCKED*abracadabra") expect(@provider.check_lock).to eql(true) end it "should return false if user is not locked" do allow(@current_resource).to receive(:password).and_return("abracadabra") expect(@provider.check_lock).to eql(false) end end describe "when locking the user" do it "should run pw lock with the new resources username" do expect(@provider).to receive(:run_command).with({ :command => "pw lock #{@new_resource.username}"}) @provider.lock_user end end describe "when unlocking the user" do it "should run pw unlock with the new resources username" do expect(@provider).to receive(:run_command).with({ :command => "pw unlock #{@new_resource.username}"}) @provider.unlock_user end end describe "when modifying the password" do before(:each) do @status = double("Status", :exitstatus => 0) allow(@provider).to receive(:popen4).and_return(@status) @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil end describe "and the new password has not been specified" do before(:each) do allow(@new_resource).to receive(:password).and_return(nil) end it "logs an appropriate message" do expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password") @provider.modify_password end end describe "and the new password has been specified" do before(:each) do allow(@new_resource).to receive(:password).and_return("abracadabra") end it "should check for differences in password between the new and current resources" do expect(@current_resource).to receive(:password) expect(@new_resource).to receive(:password) @provider.modify_password end end describe "and the passwords are identical" do before(:each) do allow(@new_resource).to receive(:password).and_return("abracadabra") allow(@current_resource).to receive(:password).and_return("abracadabra") end it "logs an appropriate message" do expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password") @provider.modify_password end end describe "and the passwords are different" do before(:each) do allow(@new_resource).to receive(:password).and_return("abracadabra") allow(@current_resource).to receive(:password).and_return("sesame") end it "should log an appropriate message" do expect(Chef::Log).to receive(:debug).with("user[adam] updating password") @provider.modify_password end it "should run pw usermod with the username and the option -H 0" do expect(@provider).to receive(:popen4).with("pw usermod adam -H 0", :waitlast => true).and_return(@status) @provider.modify_password end it "should send the new password to the stdin of pw usermod" do @stdin = StringIO.new allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) @provider.modify_password expect(@stdin.string).to eq("abracadabra\n") end it "should raise an exception if pw usermod fails" do expect(@status).to receive(:exitstatus).and_return(1) expect { @provider.modify_password }.to raise_error(Chef::Exceptions::User) end it "should not raise an exception if pw usermod succeeds" do expect(@status).to receive(:exitstatus).and_return(0) expect { @provider.modify_password }.not_to raise_error end end end describe "when loading the current state" do before do @provider.new_resource = Chef::Resource::User.new("adam") end it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do expect(File).to receive(:exists?).with("/usr/sbin/pw").and_return(false) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::User) end it "shouldn't raise an error if /usr/sbin/pw exists" do allow(File).to receive(:exists?).and_return(true) expect { @provider.load_current_resource }.not_to raise_error end end end chef-12.3.0/spec/unit/provider/remote_file_spec.rb0000644000004100000410000000411112520074675022133 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-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/provider/file' describe Chef::Provider::RemoteFile do let(:resource) do resource = Chef::Resource::RemoteFile.new("seattle", @run_context) resource.path(resource_path) resource.source("http://foo") resource.cookbook_name = "monkey" resource end let(:content) do content = double('Chef::Provider::File::Content::RemoteFile') end let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } subject(:provider) do provider = described_class.new(resource, run_context) allow(provider).to receive(:content).and_return(content) allow(provider).to receive(:update_new_resource_checksum).and_return(nil) # Otherwise it doesn't behave like a File provider provider end before do allow(Chef::FileCache).to receive(:load).with("remote_file/#{resource.name}").and_raise(Chef::Exceptions::FileNotFound) end it_behaves_like Chef::Provider::File it_behaves_like "a file provider with source field" end chef-12.3.0/spec/unit/provider/breakpoint_spec.rb0000644000004100000410000000357312520074675022012 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Breakpoint do before do @resource = Chef::Resource::Breakpoint.new @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @collection = double("resource collection") allow(@run_context).to receive(:resource_collection).and_return(@collection) @provider = Chef::Provider::Breakpoint.new(@resource, @run_context) end it "responds to load_current_resource" do expect(@provider).to respond_to(:load_current_resource) end it "gets the iterator from @collection and pauses it" do allow(Shell).to receive(:running?).and_return(true) @iterator = double("stepable_iterator") allow(@collection).to receive(:iterator).and_return(@iterator) expect(@iterator).to receive(:pause) @provider.action_break expect(@resource).to be_updated end it "doesn't pause the iterator if chef-shell isn't running" do allow(Shell).to receive(:running?).and_return(false) @iterator = double("stepable_iterator") allow(@collection).to receive(:iterator).and_return(@iterator) expect(@iterator).not_to receive(:pause) @provider.action_break end end chef-12.3.0/spec/unit/provider/log_spec.rb0000644000004100000410000000477012520074675020435 0ustar www-datawww-data# # Author:: Cary Penniman () # 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' describe Chef::Provider::Log::ChefLog do let(:log_str) { "this is my test string to log" } let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) { Chef::Resource::Log.new(log_str) } let(:provider) { Chef::Provider::Log::ChefLog.new(new_resource, run_context) } it "should write the string to the Chef::Log object at default level (info)" do expect(Chef::Log).to receive(:info).with(log_str).and_return(true) provider.run_action(:write) end it "should write the string to the Chef::Log object at debug level" do new_resource.level :debug expect(Chef::Log).to receive(:debug).with(log_str).and_return(true) provider.run_action(:write) end it "should write the string to the Chef::Log object at info level" do new_resource.level :info expect(Chef::Log).to receive(:info).with(log_str).and_return(true) provider.run_action(:write) end it "should write the string to the Chef::Log object at warn level" do new_resource.level :warn expect(Chef::Log).to receive(:warn).with(log_str).and_return(true) provider.run_action(:write) end it "should write the string to the Chef::Log object at error level" do new_resource.level :error expect(Chef::Log).to receive(:error).with(log_str).and_return(true) provider.run_action(:write) end it "should write the string to the Chef::Log object at fatal level" do new_resource.level :fatal expect(Chef::Log).to receive(:fatal).with(log_str).and_return(true) provider.run_action(:write) end it "should print the string in why-run mode" do Chef::Config[:why_run] = true expect(Chef::Log).to receive(:info).with(log_str).and_return(true) provider.run_action(:write) end end chef-12.3.0/spec/unit/provider/mount/0000755000004100000410000000000012520074675017447 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/mount/windows_spec.rb0000644000004100000410000001122412520074675022500 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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' class Chef class Util class Windows class NetUse end class Volume end end end end GUID = "\\\\?\\Volume{578e72b5-6e70-11df-b5c5-000c29d4a7d9}\\" REMOTE = "\\\\server-name\\path" describe Chef::Provider::Mount::Windows do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Mount.new("X:") @new_resource.device GUID @current_resource = Chef::Resource::Mount.new("X:") allow(Chef::Resource::Mount).to receive(:new).and_return(@current_resource) @net_use = double("Chef::Util::Windows::NetUse") allow(Chef::Util::Windows::NetUse).to receive(:new).and_return(@net_use) @vol = double("Chef::Util::Windows::Volume") allow(Chef::Util::Windows::Volume).to receive(:new).and_return(@vol) @provider = Chef::Provider::Mount::Windows.new(@new_resource, @run_context) @provider.current_resource = @current_resource end describe "when loading the current resource" do it "should set mounted true if the mount point is found" do allow(@vol).to receive(:device).and_return(@new_resource.device) expect(@current_resource).to receive(:mounted).with(true) @provider.load_current_resource end it "should set mounted false if the mount point is not found" do allow(@vol).to receive(:device).and_raise(ArgumentError) expect(@current_resource).to receive(:mounted).with(false) @provider.load_current_resource end describe "with a local device" do before do @new_resource.device GUID allow(@vol).to receive(:device).and_return(@new_resource.device) allow(@net_use).to receive(:device).and_raise(ArgumentError) end it "should determine the device is a volume GUID" do expect(@provider).to receive(:is_volume).with(@new_resource.device).and_return(true) @provider.load_current_resource end end describe "with a remote device" do before do @new_resource.device REMOTE allow(@net_use).to receive(:device).and_return(@new_resource.device) allow(@vol).to receive(:device).and_raise(ArgumentError) end it "should determine the device is remote" do expect(@provider).to receive(:is_volume).with(@new_resource.device).and_return(false) @provider.load_current_resource end end describe "when mounting a file system" do before do @new_resource.device GUID allow(@vol).to receive(:add) allow(@vol).to receive(:device).and_raise(ArgumentError) @provider.load_current_resource end it "should mount the filesystem if it is not mounted" do expect(@vol).to receive(:add).with(:remote => @new_resource.device, :username => @new_resource.username, :domainname => @new_resource.domain, :password => @new_resource.password) @provider.mount_fs end it "should not mount the filesystem if it is mounted" do expect(@vol).not_to receive(:add) allow(@current_resource).to receive(:mounted).and_return(true) @provider.mount_fs end end describe "when unmounting a file system" do before do @new_resource.device GUID allow(@vol).to receive(:delete) allow(@vol).to receive(:device).and_raise(ArgumentError) @provider.load_current_resource end it "should umount the filesystem if it is mounted" do allow(@current_resource).to receive(:mounted).and_return(true) expect(@vol).to receive(:delete) @provider.umount_fs end it "should not umount the filesystem if it is not mounted" do allow(@current_resource).to receive(:mounted).and_return(false) expect(@vol).not_to receive(:delete) @provider.umount_fs end end end end chef-12.3.0/spec/unit/provider/mount/aix_spec.rb0000644000004100000410000002005212520074675021566 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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 'ostruct' describe Chef::Provider::Mount::Aix do before(:all) do @mounted_output = <<-MOUNT node mounted mounted over vfs date options -------- --------------- --------------- ------ ------------ --------------- /dev/sdz1 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8 MOUNT @unmounted_output = <<-UNMOUNTED node mounted mounted over vfs date options -------- --------------- --------------- ------ ------------ --------------- /dev/sdz2 / jfs2 Jul 17 13:22 rw,log=/dev/hd8 UNMOUNTED @conflict_mounted_output = <<-MOUNT node mounted mounted over vfs date options -------- --------------- --------------- ------ ------------ --------------- /dev/sdz3 /tmp/foo jfs2 Jul 17 13:22 rw,log=/dev/hd8 MOUNT @enabled_output = <<-ENABLED #MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct /tmp/foo:/dev/sdz1:jfs2::bootfs:10485760:rw:yes:no ENABLED end before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Mount.new("/tmp/foo") @new_resource.device "/dev/sdz1" @new_resource.device_type :device @new_resource.fstype "jfs2" @new_resource.supports :remount => false @provider = Chef::Provider::Mount::Aix.new(@new_resource, @run_context) allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true allow(::File).to receive(:exists?).with("/tmp/foo").and_return true end def stub_mounted(provider, mounted_output) response = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => mounted_output, :stderr => "") expect(provider).to receive(:shell_out!).with("mount").and_return(response) end def stub_enabled(provider, enabled_output) response = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => enabled_output, :stderr => "") expect(provider).to receive(:shell_out).with("lsfs -c #{@new_resource.mount_point}").and_return(response) end def stub_mounted_enabled(provider, mounted_output, enabled_output) stub_mounted(provider, mounted_output) stub_enabled(provider, enabled_output) end describe "when discovering the current fs state" do it "should set current_resource.mounted to true if device is already mounted" do stub_mounted_enabled(@provider, @mounted_output, "") @provider.load_current_resource expect(@provider.current_resource.mounted).to be_truthy end it "should set current_resource.mounted to false if device is not mounted" do stub_mounted_enabled(@provider, @unmounted_output, "") @provider.load_current_resource expect(@provider.current_resource.mounted).to be_falsey end it "should set current_resource.mounted to false if the mount point is used for another device" do stub_mounted_enabled(@provider, @conflict_mounted_output, "") @provider.load_current_resource expect(@provider.current_resource.mounted).to be_falsey end end # tests for #enabled? it "should load current_resource with properties if device is already mounted and enabled" do stub_mounted_enabled(@provider, @mounted_output, @enabled_output) @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy expect(@provider.current_resource.mounted).to be_truthy expect(@provider.current_resource.mount_point).to eql(@new_resource.mount_point) expect(@provider.current_resource.fstype).to eql("jfs2") expect(@provider.current_resource.options).to eql(['rw']) end describe "mount_fs" do it "should mount resource if it is not mounted" do stub_mounted_enabled(@provider, @unmounted_output, "") expect(@provider).to receive(:shell_out!).with("mount -v #{@new_resource.fstype} #{@new_resource.device} #{@new_resource.mount_point}") @provider.run_action(:mount) end it "should not mount resource if it is already mounted" do stub_mounted_enabled(@provider, @mounted_output, "") expect(@provider).not_to receive(:mount_fs) @provider.run_action(:mount) end end describe "umount_fs" do it "should umount resource if it is already mounted" do stub_mounted_enabled(@provider, @mounted_output, "") expect(@provider).to receive(:shell_out!).with("umount #{@new_resource.mount_point}") @provider.run_action(:umount) end it "should not umount resource if it is not mounted" do stub_mounted_enabled(@provider, @unmounted_output, "") expect(@provider).not_to receive(:umount_fs) @provider.run_action(:umount) end end describe "remount_fs" do it "should remount resource if it is already mounted and it supports remounting" do @new_resource.supports({:remount => true}) stub_mounted_enabled(@provider, @mounted_output, "") expect(@provider).to receive(:shell_out!).with("mount -o remount #{@new_resource.device} #{@new_resource.mount_point}") @provider.run_action(:remount) end it "should remount with new mount options if it is already mounted and it supports remounting" do @new_resource.supports({:remount => true}) @new_resource.options("nodev,rw") stub_mounted_enabled(@provider, @mounted_output, "") expect(@provider).to receive(:shell_out!).with("mount -o remount,nodev,rw #{@new_resource.device} #{@new_resource.mount_point}") @provider.run_action(:remount) end end describe "enable_fs" do it "should enable mount if it is mounted and not enabled" do @new_resource.options("nodev,rw") stub_mounted_enabled(@provider, @mounted_output, "") filesystems = StringIO.new allow(::File).to receive(:open).with("/etc/filesystems", "a").and_yield(filesystems) @provider.run_action(:enable) expect(filesystems.string).to match(%r{^/tmp/foo:\n\tdev\t\t= /dev/sdz1\n\tvfs\t\t= jfs2\n\tmount\t\t= false\n\toptions\t\t= nodev,rw\n$}) end it "should not enable mount if it is mounted and already enabled and mount options are unchanged" do stub_mounted_enabled(@provider, @mounted_output, @enabled_output) @new_resource.options "rw" expect(@provider).not_to receive(:enable_fs) @provider.run_action(:enable) end end describe "disable_fs" do it "should disable mount if it is mounted and enabled" do stub_mounted_enabled(@provider, @mounted_output, @enabled_output) allow(::File).to receive(:open).with("/etc/filesystems", "r").and_return(<<-ETCFILESYSTEMS) /tmp/foo: dev = /dev/sdz1 vfs = jfs2 log = /dev/hd8 mount = true check = true vol = /opt free = false quota = no /tmp/abc: dev = /dev/sdz2 vfs = jfs2 mount = true options = rw ETCFILESYSTEMS filesystems = StringIO.new allow(::File).to receive(:open).with("/etc/filesystems", "w").and_yield(filesystems) @provider.run_action(:disable) expect(filesystems.string).to match(%r{^/tmp/abc:\s+dev\s+= /dev/sdz2\s+vfs\s+= jfs2\s+mount\s+= true\s+options\s+= rw\n$}) end it "should not disable mount if it is not mounted" do stub_mounted_enabled(@provider, @unmounted_output, "") expect(@provider).not_to receive(:disable_fs) @provider.run_action(:disable) end end end chef-12.3.0/spec/unit/provider/mount/mount_spec.rb0000644000004100000410000005076012520074675022160 0ustar www-datawww-data# # Author:: Joshua Timberman () # 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' require 'ostruct' describe Chef::Provider::Mount::Mount do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Mount.new("/tmp/foo") @new_resource.device "/dev/sdz1" @new_resource.device_type :device @new_resource.fstype "ext3" @new_resource.supports :remount => false @provider = Chef::Provider::Mount::Mount.new(@new_resource, @run_context) allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true allow(::File).to receive(:exists?).with("/tmp/foo").and_return true allow(::File).to receive(:realpath).with("/dev/sdz1").and_return "/dev/sdz1" allow(::File).to receive(:realpath).with("/tmp/foo").and_return "/tmp/foo" end describe "when discovering the current fs state" do before do allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => '')) allow(::File).to receive(:foreach).with("/etc/fstab") end it "should create a current resource with the same mount point and device" do @provider.load_current_resource expect(@provider.current_resource.name).to eq('/tmp/foo') expect(@provider.current_resource.mount_point).to eq('/tmp/foo') expect(@provider.current_resource.device).to eq('/dev/sdz1') end it "should accecpt device_type :uuid", :not_supported_on_solaris do @status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1) @new_resource.device_type :uuid @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a" @stdout_findfs = double("STDOUT", :first => "/dev/sdz1") expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(@status) @provider.load_current_resource() @provider.mountable? end describe "when dealing with network mounts" do { "nfs" => "nfsserver:/vol/path", "cifs" => "//cifsserver/share" }.each do |type, fs_spec| it "should detect network fs_spec (#{type})" do @new_resource.device fs_spec expect(@provider.network_device?).to be_truthy end it "should ignore trailing slash and set mounted to true for network mount (#{type})" do @new_resource.device fs_spec allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{fs_spec}/ on /tmp/foo type #{type} (rw)\n")) @provider.load_current_resource expect(@provider.current_resource.mounted).to be_truthy end end end it "should raise an error if the mount device does not exist" do allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount) end it "should not call mountable? with load_current_resource - CHEF-1565" do allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false expect(@provider).to receive(:mounted?).and_return(true) expect(@provider).to receive(:enabled?).and_return(true) expect(@provider).not_to receive(:mountable?) @provider.load_current_resource end it "should raise an error if the mount device (uuid) does not exist", :not_supported_on_solaris do status = double(:stdout => "", :exitstatus => 1) @new_resource.device_type :uuid @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a" expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status) expect(::File).to receive(:exists?).with("").and_return(false) expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount) end it "should raise an error if the mount point does not exist" do allow(::File).to receive(:exists?).with("/tmp/foo").and_return false expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount) end [ "tmpfs", "fuse", "cgroup" ].each do |fstype| it "does not expect the device to exist for #{fstype}" do @new_resource.fstype(fstype) @new_resource.device("whatever") expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error end end it "does not expect the device to exist if it's none" do @new_resource.device("none") expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error end it "should set mounted true if the mount point is found in the mounts list" do allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdz1 on /tmp/foo type ext3 (rw)\n")) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_truthy end it "should set mounted false if another mount point beginning with the same path is found in the mounts list" do allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdz1 on /tmp/foobar type ext3 (rw)\n")) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_falsey end it "should set mounted true if the symlink target of the device is found in the mounts list" do # expand the target path to correct specs on Windows target = ::File.expand_path('/dev/mapper/target') allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true) allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target) allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{target} on /tmp/foo type ext3 (rw)\n")) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_truthy end it "should set mounted true if the symlink target of the device is relative and is found in the mounts list - CHEF-4957" do target = "xsdz1" # expand the target path to correct specs on Windows absolute_target = ::File.expand_path("/dev/xsdz1") allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true) allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target) allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "#{absolute_target} on /tmp/foo type ext3 (rw)\n")) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_truthy end it "should set mounted true if the mount point is found last in the mounts list" do mount = "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n" mount << "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n" allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => mount)) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_truthy end it "should set mounted false if the mount point is not last in the mounts list" do mount = "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n" mount << "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n" allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => mount)) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_falsey end it "mounted should be false if the mount point is not found in the mounts list" do allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => "/dev/sdy1 on /tmp/foo type ext3 (rw)\n")) @provider.load_current_resource() expect(@provider.current_resource.mounted).to be_falsey end it "should set enabled to true if the mount point is last in fstab" do fstab1 = "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n" fstab2 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield(fstab1).and_yield(fstab2) @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end it "should set enabled to true if the mount point is not last in fstab and mount_point is a substring of another mount" do fstab1 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n" fstab2 = "/dev/sdy1 /tmp/foo/bar ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield(fstab1).and_yield(fstab2) @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end it "should set enabled to true if the symlink target is in fstab" do target = "/dev/mapper/target" allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true) allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target) fstab = "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end it "should set enabled to true if the symlink target is relative and is in fstab - CHEF-4957" do target = "xsdz1" allow(::File).to receive(:symlink?).with("#{@new_resource.device}").and_return(true) allow(::File).to receive(:readlink).with("#{@new_resource.device}").and_return(target) fstab = "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end it "should set enabled to false if the mount point is not in fstab" do fstab = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.enabled).to be_falsey end it "should ignore commented lines in fstab " do fstab = "\# #{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.enabled).to be_falsey end it "should set enabled to false if the mount point is not last in fstab" do line_1 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n" line_2 = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield(line_1).and_yield(line_2) @provider.load_current_resource expect(@provider.current_resource.enabled).to be_falsey end it "should not mangle the mount options if the device in fstab is a symlink" do # expand the target path to correct specs on Windows target = "/dev/mapper/target" options = "rw,noexec,noauto" allow(::File).to receive(:symlink?).with(@new_resource.device).and_return(true) allow(::File).to receive(:readlink).with(@new_resource.device).and_return(target) fstab = "#{@new_resource.device} #{@new_resource.mount_point} #{@new_resource.fstype} #{options} 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.options).to eq(options.split(',')) end it "should not mangle the mount options if the symlink target is in fstab" do target = ::File.expand_path("/dev/mapper/target") options = "rw,noexec,noauto" allow(::File).to receive(:symlink?).with(@new_resource.device).and_return(true) allow(::File).to receive(:readlink).with(@new_resource.device).and_return(target) fstab = "#{target} #{@new_resource.mount_point} #{@new_resource.fstype} #{options} 1 2\n" allow(::File).to receive(:foreach).with("/etc/fstab").and_yield fstab @provider.load_current_resource expect(@provider.current_resource.options).to eq(options.split(',')) end end context "after the mount's state has been discovered" do before do @current_resource = Chef::Resource::Mount.new("/tmp/foo") @current_resource.device "/dev/sdz1" @current_resource.device_type :device @current_resource.fstype "ext3" @provider.current_resource = @current_resource end describe "mount_fs" do it "should mount the filesystem if it is not mounted" do expect(@provider).to receive(:shell_out!).with("mount -t ext3 -o defaults /dev/sdz1 /tmp/foo") @provider.mount_fs() end it "should mount the filesystem with options if options were passed" do options = "rw,noexec,noauto" @new_resource.options(%w{rw noexec noauto}) expect(@provider).to receive(:shell_out!).with("mount -t ext3 -o rw,noexec,noauto /dev/sdz1 /tmp/foo") @provider.mount_fs() end it "should mount the filesystem specified by uuid", :not_supported_on_solaris do status = double(:stdout => "/dev/sdz1\n", :exitstatus => 1) @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a" @new_resource.device_type :uuid allow(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status) @stdout_mock = double('stdout mock') allow(@stdout_mock).to receive(:each).and_yield("#{@new_resource.device} on #{@new_resource.mount_point}") expect(@provider).to receive(:shell_out!).with("mount -t #{@new_resource.fstype} -o defaults -U #{@new_resource.device} #{@new_resource.mount_point}").and_return(@stdout_mock) @provider.mount_fs() end it "should not mount the filesystem if it is mounted" do allow(@current_resource).to receive(:mounted).and_return(true) expect(@provider).not_to receive(:shell_out!) @provider.mount_fs() end end describe "umount_fs" do it "should umount the filesystem if it is mounted" do @current_resource.mounted(true) expect(@provider).to receive(:shell_out!).with("umount /tmp/foo") @provider.umount_fs() end it "should not umount the filesystem if it is not mounted" do @current_resource.mounted(false) expect(@provider).not_to receive(:shell_out!) @provider.umount_fs() end end describe "remount_fs" do it "should use mount -o remount if remount is supported" do @new_resource.supports({:remount => true}) @current_resource.mounted(true) expect(@provider).to receive(:shell_out!).with("mount -o remount,defaults #{@new_resource.mount_point}") @provider.remount_fs end it "should use mount -o remount with new mount options if remount is supported" do @new_resource.supports({:remount => true}) options = "rw,noexec,noauto" @new_resource.options(%w{rw noexec noauto}) @current_resource.mounted(true) expect(@provider).to receive(:shell_out!).with("mount -o remount,rw,noexec,noauto #{@new_resource.mount_point}") @provider.remount_fs end it "should umount and mount if remount is not supported" do @new_resource.supports({:remount => false}) @current_resource.mounted(true) expect(@provider).to receive(:umount_fs) expect(@provider).to receive(:sleep).with(1) expect(@provider).to receive(:mount_fs) @provider.remount_fs() end it "should not try to remount at all if mounted is false" do @current_resource.mounted(false) expect(@provider).not_to receive(:shell_out!) expect(@provider).not_to receive(:umount_fs) expect(@provider).not_to receive(:mount_fs) @provider.remount_fs() end end describe "when enabling the fs" do it "should enable if enabled isn't true" do @current_resource.enabled(false) @fstab = StringIO.new allow(::File).to receive(:open).with("/etc/fstab", "a").and_yield(@fstab) @provider.enable_fs expect(@fstab.string).to match(%r{^/dev/sdz1\s+/tmp/foo\s+ext3\s+defaults\s+0\s+2\s*$}) end it "should not enable if enabled is true and resources match" do @current_resource.enabled(true) @current_resource.fstype("ext3") @current_resource.options(["defaults"]) @current_resource.dump(0) @current_resource.pass(2) expect(::File).not_to receive(:open).with("/etc/fstab", "a") @provider.enable_fs end it "should enable if enabled is true and resources do not match" do @current_resource.enabled(true) @current_resource.fstype("auto") @current_resource.options(["defaults"]) @current_resource.dump(0) @current_resource.pass(2) @fstab = StringIO.new allow(::File).to receive(:readlines).and_return([]) expect(::File).to receive(:open).once.with("/etc/fstab", "w").and_yield(@fstab) expect(::File).to receive(:open).once.with("/etc/fstab", "a").and_yield(@fstab) @provider.enable_fs end end describe "when disabling the fs" do it "should disable if enabled is true" do @current_resource.enabled(true) other_mount = "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n" this_mount = "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n" @fstab_read = [this_mount, other_mount] allow(::File).to receive(:readlines).with("/etc/fstab").and_return(@fstab_read) @fstab_write = StringIO.new allow(::File).to receive(:open).with("/etc/fstab", "w").and_yield(@fstab_write) @provider.disable_fs expect(@fstab_write.string).to match(Regexp.escape(other_mount)) expect(@fstab_write.string).not_to match(Regexp.escape(this_mount)) end it "should disable if enabled is true and ignore commented lines" do @current_resource.enabled(true) fstab_read = [%q{/dev/sdy1 /tmp/foo ext3 defaults 1 2}, %q{/dev/sdz1 /tmp/foo ext3 defaults 1 2}, %q{#/dev/sdz1 /tmp/foo ext3 defaults 1 2}] fstab_write = StringIO.new allow(::File).to receive(:readlines).with("/etc/fstab").and_return(fstab_read) allow(::File).to receive(:open).with("/etc/fstab", "w").and_yield(fstab_write) @provider.disable_fs expect(fstab_write.string).to match(%r{^/dev/sdy1 /tmp/foo ext3 defaults 1 2$}) expect(fstab_write.string).to match(%r{^#/dev/sdz1 /tmp/foo ext3 defaults 1 2$}) expect(fstab_write.string).not_to match(%r{^/dev/sdz1 /tmp/foo ext3 defaults 1 2$}) end it "should disable only the last entry if enabled is true" do allow(@current_resource).to receive(:enabled).and_return(true) fstab_read = ["/dev/sdz1 /tmp/foo ext3 defaults 1 2\n", "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n", "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n", "/dev/sdz1 /tmp/foobar ext3 defaults 1 2\n"] fstab_write = StringIO.new allow(::File).to receive(:readlines).with("/etc/fstab").and_return(fstab_read) allow(::File).to receive(:open).with("/etc/fstab", "w").and_yield(fstab_write) @provider.disable_fs expect(fstab_write.string).to eq("/dev/sdz1 /tmp/foo ext3 defaults 1 2\n" + "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n" + "/dev/sdz1 /tmp/foobar ext3 defaults 1 2\n") end it "should not disable if enabled is false" do allow(@current_resource).to receive(:enabled).and_return(false) allow(::File).to receive(:readlines).with("/etc/fstab").and_return([]) expect(::File).not_to receive(:open).and_yield(@fstab) @provider.disable_fs end end end end chef-12.3.0/spec/unit/provider/mount/solaris_spec.rb0000644000004100000410000007260312520074675022472 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'ostruct' # Do not run these tests on windows because some path handling # code is not implemented to handle windows paths. describe Chef::Provider::Mount::Solaris, :unix_only do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:device_type) { :device } let(:fstype) { "ufs" } let(:device) { "/dev/dsk/c0t2d0s7" } let(:fsck_device) { "/dev/rdsk/c0t2d0s7" } let(:mountpoint) { "/mnt/foo" } let(:options) { nil } let(:new_resource) { new_resource = Chef::Resource::Mount.new(mountpoint) new_resource.device device new_resource.device_type device_type new_resource.fsck_device fsck_device new_resource.fstype fstype new_resource.options options new_resource.supports :remount => false new_resource } let(:provider) { Chef::Provider::Mount::Solaris.new(new_resource, run_context) } let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' #device device mount FS fsck mount mount #to mount to fsck point type pass at boot options # fd - /dev/fd fd - no - /proc - /proc proc - no - # swap /dev/dsk/c0t0d0s1 - - swap - no - # root /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no - # tmpfs swap - /tmp tmpfs - yes - # nfs cartman:/share2 - /cartman nfs - yes rw,soft # ufs /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } let(:vfstab_file) { t = Tempfile.new("rspec-vfstab") t.write(vfstab_file_contents) t.close t } let(:mount_output) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t0d0s0 on / type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012 /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } before do stub_const("Chef::Provider::Mount::Solaris::VFSTAB", vfstab_file.path ) allow(provider).to receive(:shell_out!).with("mount -v").and_return(OpenStruct.new(:stdout => mount_output)) allow(File).to receive(:symlink?).with(device).and_return(false) allow(File).to receive(:exist?).and_call_original # Tempfile.open on ruby 1.8.7 calls File.exist? allow(File).to receive(:exist?).with(device).and_return(true) allow(File).to receive(:exist?).with(mountpoint).and_return(true) expect(File).to_not receive(:exists?) end describe "#define_resource_requirements" do before do # we're not testing the actual actions so stub them all out [:mount_fs, :umount_fs, :remount_fs, :enable_fs, :disable_fs].each {|m| allow(provider).to receive(m) } end it "run_action(:mount) should raise an error if the device does not exist" do allow(File).to receive(:exist?).with(device).and_return(false) expect { provider.run_action(:mount) }.to raise_error(Chef::Exceptions::Mount) end it "run_action(:remount) should raise an error if the device does not exist" do allow(File).to receive(:exist?).with(device).and_return(false) expect { provider.run_action(:remount) }.to raise_error(Chef::Exceptions::Mount) end it "run_action(:mount) should raise an error if the mountpoint does not exist" do allow(File).to receive(:exist?).with(mountpoint).and_return false expect { provider.run_action(:mount) }.to raise_error(Chef::Exceptions::Mount) end it "run_action(:remount) should raise an error if the mountpoint does not exist" do allow(File).to receive(:exist?).with(mountpoint).and_return false expect { provider.run_action(:remount) }.to raise_error(Chef::Exceptions::Mount) end %w{tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs vxfs}.each do |ft| context "when the device has a fstype of #{ft}" do let(:fstype) { ft } let(:fsck_device) { "-" } let(:device) { "something_that_is_not_a_file" } before do expect(File).to_not receive(:exist?).with(device) end it "run_action(:mount) should not raise an error" do expect { provider.run_action(:mount) }.to_not raise_error end it "run_action(:remount) should not raise an error" do expect { provider.run_action(:remount) }.to_not raise_error end end end end describe "#load_current_resource" do context "when loading a normal UFS filesystem with mount at boot" do before do provider.load_current_resource end it "should create a current_resource of type Chef::Resource::Mount" do expect(provider.current_resource).to be_a(Chef::Resource::Mount) end it "should set the name on the current_resource" do expect(provider.current_resource.name).to eq(mountpoint) end it "should set the mount_point on the current_resource" do expect(provider.current_resource.mount_point).to eq(mountpoint) end it "should set the device on the current_resource" do expect(provider.current_resource.device).to eq(device) end it "should set the fsck_device on the current_resource" do expect(provider.current_resource.fsck_device).to eq(fsck_device) end it "should set the device_type on the current_resource" do expect(provider.current_resource.device_type).to eq(device_type) end it "should set the mounted status on the current_resource" do expect(provider.current_resource.mounted).to be_truthy end it "should set the enabled status on the current_resource" do expect(provider.current_resource.enabled).to be_truthy end it "should set the fstype field on the current_resource" do expect(provider.current_resource.fstype).to eql("ufs") end it "should set the options field on the current_resource" do expect(provider.current_resource.options).to eql(["-"]) end it "should set the pass field on the current_resource" do expect(provider.current_resource.pass).to eql(2) end it "should not throw an exception when the device does not exist - CHEF-1565" do allow(File).to receive(:exist?).with(device).and_return(false) expect { provider.load_current_resource }.to_not raise_error end it "should not throw an exception when the mount point does not exist" do allow(File).to receive(:exist?).with(mountpoint).and_return false expect { provider.load_current_resource }.to_not raise_error end end end describe "#load_current_resource" do context "when loading a normal UFS filesystem with noauto, don't mount at boot" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' #device device mount FS fsck mount mount #to mount to fsck point type pass at boot options # fd - /dev/fd fd - no - /proc - /proc proc - no - # swap /dev/dsk/c0t0d0s1 - - swap - no - # root /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no - # tmpfs swap - /tmp tmpfs - yes - # nfs cartman:/share2 - /cartman nfs - yes rw,soft # ufs /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 no - EOF } before do provider.load_current_resource end it "should set the options field on the current_resource" do expect(provider.current_resource.options).to eql(["-", "noauto"]) end end context "when the device is an smbfs mount" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' //solarsystem/tmp on /mnt type smbfs read/write/setuid/devices/dev=5080000 on Tue Mar 29 11:40:18 2011 EOF } let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' //WORKGROUP;username:password@host/share - /mountpoint smbfs - no fileperms=0777,dirperms=0777 EOF } let(:fsck_device) { "-" } it "should work at some point in the future" do skip "SMBFS mounts on solaris look like they will need some future code work and more investigation" end end context "when the device is an NFS mount" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' cartman:/share2 on /cartman type nfs rsize=32768,wsize=32768,NFSv4,dev=4000004 on Tue Mar 29 11:40:18 2011 EOF } let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' cartman:/share2 - /cartman nfs - yes rw,soft EOF } let(:fsck_device) { "-" } let(:fstype) { "nfs" } let(:device) { "cartman:/share2" } let(:mountpoint) { "/cartman" } before do provider.load_current_resource end it "should set the name on the current_resource" do expect(provider.current_resource.name).to eq(mountpoint) end it "should set the mount_point on the current_resource" do expect(provider.current_resource.mount_point).to eq(mountpoint) end it "should set the device on the current_resource" do expect(provider.current_resource.device).to eq(device) end it "should set the device_type on the current_resource" do expect(provider.current_resource.device_type).to eq(device_type) end it "should set the mounted status on the current_resource" do expect(provider.current_resource.mounted).to be_truthy end it "should set the enabled status on the current_resource" do expect(provider.current_resource.enabled).to be_truthy end it "should set the fstype field on the current_resource" do expect(provider.current_resource.fstype).to eql("nfs") end it "should set the options field on the current_resource" do expect(provider.current_resource.options).to eql(["rw", "soft"]) end it "should set the pass field on the current_resource" do # is this correct or should it be nil? # # vfstab man page says. # "A - is used to indicate no entry in a field." # 0 and - could mean different things for some file systems expect(provider.current_resource.pass).to eql(0) end end context "when the device is symlink" do let(:target) { "/dev/mapper/target" } let(:mount_output) { <<-EOF.gsub /^\s*/, '' #{target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' #{target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } before do expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true) expect(File).to receive(:readlink).with(device).at_least(:once).and_return(target) provider.load_current_resource() end it "should set mounted true if the symlink target of the device is found in the mounts list" do expect(provider.current_resource.mounted).to be_truthy end it "should set enabled true if the symlink target of the device is found in the vfstab" do expect(provider.current_resource.enabled).to be_truthy end it "should have the correct mount options" do expect(provider.current_resource.options).to eql(["-"]) end end context "when the device is a relative symlink" do let(:target) { "foo" } let(:absolute_target) { File.expand_path(target, File.dirname(device)) } let(:mount_output) { <<-EOF.gsub /^\s*/, '' #{absolute_target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' #{absolute_target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } before do expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true) expect(File).to receive(:readlink).with(device).at_least(:once).and_return(target) provider.load_current_resource() end it "should set mounted true if the symlink target of the device is found in the mounts list" do expect(provider.current_resource.mounted).to be_truthy end it "should set enabled true if the symlink target of the device is found in the vfstab" do expect(provider.current_resource.enabled).to be_truthy end it "should have the correct mount options" do expect(provider.current_resource.options).to eql(["-"]) end end context "when the matching mount point is last in the mounts list" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012 /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } it "should set mounted true" do provider.load_current_resource() expect(provider.current_resource.mounted).to be_truthy end end context "when the matching mount point is not last in the mounts list" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012 EOF } it "should set mounted false" do provider.load_current_resource() expect(provider.current_resource.mounted).to be_falsey end end context "when the matching mount point is not in the mounts list (mountpoint wrong)" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s7 on /mnt/foob type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } it "should set mounted false" do provider.load_current_resource() expect(provider.current_resource.mounted).to be_falsey end end context "when the matching mount point is not in the mounts list (raw device wrong)" do let(:mount_output) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s72 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012 EOF } it "should set mounted false" do provider.load_current_resource() expect(provider.current_resource.mounted).to be_falsey end end context "when the mount point is last in fstab" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } it "should set enabled to true" do provider.load_current_resource expect(provider.current_resource.enabled).to be_truthy end end context "when the mount point is not last in fstab and is a substring of another mount" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo/bar ufs 2 yes - EOF } it "should set enabled to true" do provider.load_current_resource expect(provider.current_resource.enabled).to be_truthy end end context "when the mount point is not last in fstab" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s72 /mnt/foo ufs 2 yes - EOF } it "should set enabled to false" do provider.load_current_resource expect(provider.current_resource.enabled).to be_falsey end end context "when the mount point is not in fstab, but the mountpoint is a substring of one that is" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foob ufs 2 yes - EOF } it "should set enabled to false" do provider.load_current_resource expect(provider.current_resource.enabled).to be_falsey end end context "when the mount point is not in fstab, but the device is a substring of one that is" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } it "should set enabled to false" do provider.load_current_resource expect(provider.current_resource.enabled).to be_falsey end end context "when the mountpoint line is commented out" do let(:vfstab_file_contents) { <<-EOF.gsub /^\s*/, '' #/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes - EOF } it "should set enabled to false" do provider.load_current_resource expect(provider.current_resource.enabled).to be_falsey end end end context "after the mount's state has been discovered" do describe "mount_fs" do it "should mount the filesystem" do expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o defaults #{device} #{mountpoint}") provider.mount_fs() end it "should mount the filesystem with options if options were passed" do options = "logging,noatime,largefiles,nosuid,rw,quota" new_resource.options(options.split(/,/)) expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o #{options} #{device} #{mountpoint}") provider.mount_fs() end it "should delete the 'noauto' magic option" do options = "rw,noauto" new_resource.options(%w{rw noauto}) expect(provider).to receive(:shell_out!).with("mount -F #{fstype} -o rw #{device} #{mountpoint}") provider.mount_fs() end end describe "umount_fs" do it "should umount the filesystem if it is mounted" do expect(provider).to receive(:shell_out!).with("umount #{mountpoint}") provider.umount_fs() end end describe "remount_fs without options and do not mount at boot" do it "should use mount -o remount" do new_resource.options(%w{noauto}) expect(provider).to receive(:shell_out!).with("mount -o remount #{new_resource.mount_point}") provider.remount_fs end end describe "remount_fs with options and do not mount at boot" do it "should use mount -o remount,rw" do new_resource.options(%w{rw noauto}) expect(provider).to receive(:shell_out!).with("mount -o remount,rw #{new_resource.mount_point}") provider.remount_fs end end describe "remount_fs with options and mount at boot" do it "should use mount -o remount,rw" do new_resource.options(%w{rw}) expect(provider).to receive(:shell_out!).with("mount -o remount,rw #{new_resource.mount_point}") provider.remount_fs end end describe "remount_fs without options and mount at boot" do it "should use mount -o remount" do new_resource.options([]) expect(provider).to receive(:shell_out!).with("mount -o remount #{new_resource.mount_point}") provider.remount_fs end end describe "when enabling the fs" do context "in the typical case" do let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\tdefaults\n" } let(:vfstab_file_contents) { [other_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.enable_fs end it "should leave the other mountpoint alone" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(other_mount)}/) end it "should enable the mountpoint we care about" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(this_mount)}/) end end context "when the mount has options=noauto" do let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tno\t-\n" } let(:options) { "noauto" } let(:vfstab_file_contents) { [other_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.enable_fs end it "should leave the other mountpoint alone" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(other_mount)}/) end it "should enable the mountpoint we care about" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(this_mount)}/) end end context "when the new mount has options of noauto and the existing mount has mount at boot yes" do let(:existing_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\t-" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tno\t-\n" } let(:options) { "noauto" } let(:vfstab_file_contents) { [existing_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.mount_options_unchanged? provider.send(:vfstab_entry) end it "should detect a changed entry" do expect(provider.mount_options_unchanged?).to eq(false) end it "should change mount at boot to no" do expect(provider.send(:vfstab_entry)).to match(/^#{Regexp.escape(this_mount)}/) end end context "when the new mount has options of - and the existing mount has mount at boot no" do let(:existing_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tno\t-" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\t-\n" } let(:options) { "-" } let(:vfstab_file_contents) { [existing_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.mount_options_unchanged? provider.send(:vfstab_entry) end it "should detect a changed entry" do expect(provider.mount_options_unchanged?).to eq(false) end it "should change mount at boot to yes" do expect(provider.send(:vfstab_entry)).to match(/^#{Regexp.escape(this_mount)}/) end end context "when the new mount has options of noauto and the existing mount has mount at boot no" do let(:existing_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tno\t-" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tno\t-\n" } let(:options) { "-,noauto" } let(:vfstab_file_contents) { [existing_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.mount_options_unchanged? provider.send(:vfstab_entry) end it "should detect an unchanged entry" do expect(provider.mount_options_unchanged?).to eq(true) end it "should not change mount at boot" do expect(provider.send(:vfstab_entry)).to match(/^#{Regexp.escape(this_mount)}/) end end context "when the new mount has options of - and the existing mount has mount at boot yes" do let(:existing_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\t-" } let(:this_mount) { "/dev/dsk/c0t2d0s7\t/dev/rdsk/c0t2d0s7\t/mnt/foo\tufs\t2\tyes\t-\n" } let(:options) { "-" } let(:vfstab_file_contents) { [existing_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.load_current_resource provider.mount_options_unchanged? provider.send(:vfstab_entry) end it "should detect an unchanged entry" do expect(provider.mount_options_unchanged?).to eq(true) end it "should not change mount at boot" do expect(provider.send(:vfstab_entry)).to match(/^#{Regexp.escape(this_mount)}/) end end end describe "when disabling the fs" do context "in the typical case" do let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" } let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" } let(:vfstab_file_contents) { [other_mount, this_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.disable_fs end it "should leave the other mountpoint alone" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(other_mount)}/) end it "should disable the mountpoint we care about" do expect(IO.read(vfstab_file.path)).not_to match(/^#{Regexp.escape(this_mount)}/) end end context "when there is a commented out line" do let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" } let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" } let(:comment) { "#/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" } let(:vfstab_file_contents) { [other_mount, this_mount, comment].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.disable_fs end it "should leave the other mountpoint alone" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(other_mount)}/) end it "should disable the mountpoint we care about" do expect(IO.read(vfstab_file.path)).not_to match(/^#{Regexp.escape(this_mount)}/) end it "should keep the comment" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(comment)}/) end end context "when there is a duplicated line" do let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" } let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" } let(:vfstab_file_contents) { [this_mount, other_mount, this_mount].join("\n") } before do allow(provider).to receive(:etc_tempfile).and_yield(Tempfile.open("vfstab")) provider.disable_fs end it "should leave the other mountpoint alone" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(other_mount)}/) end it "should still match the duplicated mountpoint" do expect(IO.read(vfstab_file.path)).to match(/^#{Regexp.escape(this_mount)}/) end it "should have removed the last line" do expect(IO.read(vfstab_file.path)).to eql( "#{this_mount}\n#{other_mount}\n" ) end end end end end chef-12.3.0/spec/unit/provider/dsc_resource_spec.rb0000644000004100000410000000577712520074675022344 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'spec_helper' describe Chef::Provider::DscResource do let (:events) { Chef::EventDispatch::Dispatcher.new } let (:run_context) { Chef::RunContext.new(node, {}, events) } let (:resource) { Chef::Resource::DscResource.new("dscresource", run_context) } let (:provider) do Chef::Provider::DscResource.new(resource, run_context) end context 'when Powershell does not support Invoke-DscResource' do let (:node) { node = Chef::Node.new node.automatic[:languages][:powershell][:version] = '4.0' node } it 'raises a NoProviderAvailable exception' do expect(provider).not_to receive(:meta_configuration) expect{provider.run_action(:run)}.to raise_error( Chef::Exceptions::NoProviderAvailable, /5\.0\.10018\.0/) end end context 'when Powershell supports Invoke-DscResource' do let (:node) { node = Chef::Node.new node.automatic[:languages][:powershell][:version] = '5.0.10018.0' node } context 'when RefreshMode is not set to Disabled' do let (:meta_configuration) { {'RefreshMode' => 'AnythingElse'}} it 'raises an exception' do expect(provider).to receive(:meta_configuration).and_return( meta_configuration) expect { provider.run_action(:run) }.to raise_error( Chef::Exceptions::NoProviderAvailable, /Disabled/) end end context 'when RefreshMode is set to Disabled' do let (:meta_configuration) { {'RefreshMode' => 'Disabled'}} it 'does not update the resource if it is up to date' do expect(provider).to receive(:meta_configuration).and_return( meta_configuration) expect(provider).to receive(:test_resource).and_return(true) provider.run_action(:run) expect(resource).not_to be_updated end it 'converges the resource if it is not up to date' do expect(provider).to receive(:meta_configuration).and_return( meta_configuration) expect(provider).to receive(:test_resource).and_return(false) expect(provider).to receive(:set_resource) provider.run_action(:run) expect(resource).to be_updated end end end end chef-12.3.0/spec/unit/provider/cookbook_file/0000755000004100000410000000000012520074675021112 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/cookbook_file/content_spec.rb0000644000004100000410000000270712520074675024131 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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::CookbookFile::Content do let(:new_resource) { double('Chef::Resource::CookbookFile (new)', :cookbook_name => 'apache2', :cookbook => 'apache2') } let(:content) do @run_context = double('Chef::RunContext') @current_resource = double('Chef::Resource::CookbookFile (current)') Chef::Provider::CookbookFile::Content.new(new_resource, @current_resource, @run_context) end it "prefers the explicit cookbook name on the resource to the implicit one" do allow(new_resource).to receive(:cookbook).and_return('nginx') expect(content.send(:resource_cookbook)).to eq('nginx') end it "falls back to the implicit cookbook name on the resource" do expect(content.send(:resource_cookbook)).to eq('apache2') end end chef-12.3.0/spec/unit/provider/erl_call_spec.rb0000644000004100000410000000537512520074675021433 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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::ErlCall do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::ErlCall.new("test", @node) @new_resource.code("io:format(\"burritos\", []).") @new_resource.node_name("chef@localhost") @new_resource.name("test") @provider = Chef::Provider::ErlCall.new(@new_resource, @run_context) allow(@provider).to receive(:popen4).and_return(@status) @stdin = StringIO.new @stdout = StringIO.new('{ok, woohoo}') @stderr = StringIO.new @pid = 2342999 end it "should return a Chef::Provider::ErlCall object" do provider = Chef::Provider::ErlCall.new(@new_resource, @run_context) expect(provider).to be_a_kind_of(Chef::Provider::ErlCall) end it "should return true" do expect(@provider.load_current_resource).to eql(true) end describe "when running a distributed erl call resource" do before do @new_resource.cookie("nomnomnom") @new_resource.distributed(true) @new_resource.name_type("sname") end it "should write to stdin of the erl_call command" do expected_cmd = "erl_call -e -s -sname chef@localhost -c nomnomnom" expect(@provider).to receive(:popen4).with(expected_cmd, :waitlast => true).and_return([@pid, @stdin, @stdout, @stderr]) expect(Process).to receive(:wait).with(@pid) @provider.action_run expect(@stdin.string).to eq("#{@new_resource.code}\n") end end describe "when running a local erl call resource" do before do @new_resource.cookie(nil) @new_resource.distributed(false) @new_resource.name_type("name") end it "should write to stdin of the erl_call command" do expect(@provider).to receive(:popen4).with("erl_call -e -name chef@localhost ", :waitlast => true).and_return([@pid, @stdin, @stdout, @stderr]) expect(Process).to receive(:wait).with(@pid) @provider.action_run expect(@stdin.string).to eq("#{@new_resource.code}\n") end end end chef-12.3.0/spec/unit/provider/file_spec.rb0000644000004100000410000000342412520074675020566 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-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 'support/shared/unit/provider/file' describe Chef::Provider::File do let(:resource) do # need to check for/against mutating state within the new_resource, so don't mock resource = Chef::Resource::File.new("seattle") resource.path(resource_path) resource end let(:content) do content = double('Chef::Provider::File::Content') end let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } # Subject let(:provider) do provider = described_class.new(resource, run_context) allow(provider).to receive(:content).and_return(content) provider end it_behaves_like Chef::Provider::File it_behaves_like "a file provider with content field" end chef-12.3.0/spec/unit/provider/mdadm_spec.rb0000644000004100000410000001261412520074675020732 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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 'ostruct' describe Chef::Provider::Mdadm do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Mdadm.new('/dev/md1') @new_resource.devices ["/dev/sdz1","/dev/sdz2","/dev/sdz3"] @provider = Chef::Provider::Mdadm.new(@new_resource, @run_context) end describe "when determining the current metadevice status" do it "should set the current resources mount point to the new resources mount point" do allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(:status => 0)) @provider.load_current_resource expect(@provider.current_resource.name).to eq('/dev/md1') expect(@provider.current_resource.raid_device).to eq('/dev/md1') end it "determines that the metadevice exists when mdadm exit code is zero" do allow(@provider).to receive(:shell_out!).with("mdadm --detail --test /dev/md1", :returns => [0,4]).and_return(OpenStruct.new(:status => 0)) @provider.load_current_resource expect(@provider.current_resource.exists).to be_truthy end it "determines that the metadevice does not exist when mdadm exit code is 4" do allow(@provider).to receive(:shell_out!).with("mdadm --detail --test /dev/md1", :returns => [0,4]).and_return(OpenStruct.new(:status => 4)) @provider.load_current_resource expect(@provider.current_resource.exists).to be_falsey end end describe "after the metadevice status is known" do before(:each) do @current_resource = Chef::Resource::Mdadm.new('/dev/md1') @new_resource.level 5 allow(@provider).to receive(:load_current_resource).and_return(true) @provider.current_resource = @current_resource end describe "when creating the metadevice" do it "should create the raid device if it doesnt exist" do @current_resource.exists(false) expected_command = "yes | mdadm --create /dev/md1 --level 5 --chunk=16 --metadata=0.90 --raid-devices 3 /dev/sdz1 /dev/sdz2 /dev/sdz3" expect(@provider).to receive(:shell_out!).with(expected_command) @provider.run_action(:create) end it "should specify a bitmap only if set" do @current_resource.exists(false) @new_resource.bitmap('grow') expected_command = "yes | mdadm --create /dev/md1 --level 5 --chunk=16 --metadata=0.90 --bitmap=grow --raid-devices 3 /dev/sdz1 /dev/sdz2 /dev/sdz3" expect(@provider).to receive(:shell_out!).with(expected_command) @provider.run_action(:create) expect(@new_resource).to be_updated_by_last_action end it "should not specify a chunksize if raid level 1" do @current_resource.exists(false) @new_resource.level 1 expected_command = "yes | mdadm --create /dev/md1 --level 1 --metadata=0.90 --raid-devices 3 /dev/sdz1 /dev/sdz2 /dev/sdz3" expect(@provider).to receive(:shell_out!).with(expected_command) @provider.run_action(:create) expect(@new_resource).to be_updated_by_last_action end it "should not create the raid device if it does exist" do @current_resource.exists(true) expect(@provider).not_to receive(:shell_out!) @provider.run_action(:create) expect(@new_resource).not_to be_updated_by_last_action end end describe "when asembling the metadevice" do it "should assemble the raid device if it doesnt exist" do @current_resource.exists(false) expected_mdadm_cmd = "yes | mdadm --assemble /dev/md1 /dev/sdz1 /dev/sdz2 /dev/sdz3" expect(@provider).to receive(:shell_out!).with(expected_mdadm_cmd) @provider.run_action(:assemble) expect(@new_resource).to be_updated_by_last_action end it "should not assemble the raid device if it doesnt exist" do @current_resource.exists(true) expect(@provider).not_to receive(:shell_out!) @provider.run_action(:assemble) expect(@new_resource).not_to be_updated_by_last_action end end describe "when stopping the metadevice" do it "should stop the raid device if it exists" do @current_resource.exists(true) expected_mdadm_cmd = "yes | mdadm --stop /dev/md1" expect(@provider).to receive(:shell_out!).with(expected_mdadm_cmd) @provider.run_action(:stop) expect(@new_resource).to be_updated_by_last_action end it "should not attempt to stop the raid device if it does not exist" do @current_resource.exists(false) expect(@provider).not_to receive(:shell_out!) @provider.run_action(:stop) expect(@new_resource).not_to be_updated_by_last_action end end end end chef-12.3.0/spec/unit/provider/service/0000755000004100000410000000000012520074675017745 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/service/debian_service_spec.rb0000644000004100000410000003177212520074675024260 0ustar www-datawww-data# # Author:: AJ Christensen () # Copyright:: Copyright (c) 2008 HJK Solutions, LLC # 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::Service::Debian do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => 'fuuuu'} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Debian.new(@new_resource, @run_context) @current_resource = Chef::Resource::Service.new("chef") @provider.current_resource = @current_resource @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil end describe "load_current_resource" do it "ensures /usr/sbin/update-rc.d is available" do expect(File).to receive(:exists?).with("/usr/sbin/update-rc.d") .and_return(false) @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end context "when update-rc.d shows init linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) result = <<-UPDATE_RC_D_SUCCESS Removing any system startup links for /etc/init.d/chef ... /etc/rc0.d/K20chef /etc/rc1.d/K20chef /etc/rc2.d/S20chef /etc/rc3.d/S20chef /etc/rc4.d/S20chef /etc/rc5.d/S20chef /etc/rc6.d/K20chef UPDATE_RC_D_SUCCESS @stdout = StringIO.new(result) @stderr = StringIO.new @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) end it "says the service is enabled" do expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_truthy end it "stores the 'enabled' state" do allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) expect(@provider.load_current_resource).to equal(@current_resource) expect(@current_resource.enabled).to be_truthy end end context "when update-rc.d shows init isn't linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) @status = double("Status", :exitstatus => 0) @stdout = StringIO.new( " Removing any system startup links for /etc/init.d/chef ...") @stderr = StringIO.new @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) end it "says the service is disabled" do expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_falsey end it "stores the 'disabled' state" do allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) expect(@provider.load_current_resource).to equal(@current_resource) expect(@current_resource.enabled).to be_falsey end end context "when update-rc.d fails" do before do @status = double("Status", :exitstatus => -1) allow(@provider).to receive(:popen4).and_return(@status) end it "raises an error" do @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end {"Debian/Lenny and older" => { "linked" => { "stdout" => <<-STDOUT, Removing any system startup links for /etc/init.d/chef ... /etc/rc0.d/K20chef /etc/rc1.d/K20chef /etc/rc2.d/S20chef /etc/rc3.d/S20chef /etc/rc4.d/S20chef /etc/rc5.d/S20chef /etc/rc6.d/K20chef STDOUT "stderr" => "", "priorities" => { "0"=>[:stop, "20"], "1"=>[:stop, "20"], "2"=>[:start, "20"], "3"=>[:start, "20"], "4"=>[:start, "20"], "5"=>[:start, "20"], "6"=>[:stop, "20"] } }, "not linked" => { "stdout" => " Removing any system startup links for /etc/init.d/chef ...", "stderr" => "" }, }, "Debian/Squeeze and earlier" => { "linked" => { "stdout" => "update-rc.d: using dependency based boot sequencing", "stderr" => <<-STDERR, insserv: remove service /etc/init.d/../rc0.d/K20chef-client insserv: remove service /etc/init.d/../rc1.d/K20chef-client insserv: remove service /etc/init.d/../rc2.d/S20chef-client insserv: remove service /etc/init.d/../rc3.d/S20chef-client insserv: remove service /etc/init.d/../rc4.d/S20chef-client insserv: remove service /etc/init.d/../rc5.d/S20chef-client insserv: remove service /etc/init.d/../rc6.d/K20chef-client insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop STDERR "priorities" => { "0"=>[:stop, "20"], "1"=>[:stop, "20"], "2"=>[:start, "20"], "3"=>[:start, "20"], "4"=>[:start, "20"], "5"=>[:start, "20"], "6"=>[:stop, "20"] } }, "not linked" => { "stdout" => "update-rc.d: using dependency based boot sequencing", "stderr" => "" } }, "Debian/Wheezy and earlier, a service only starting at run level S" => { "linked" => { "stdout" => "", "stderr" => <<-STDERR, insserv: remove service /etc/init.d/../rc0.d/K06rpcbind insserv: remove service /etc/init.d/../rc1.d/K06rpcbind insserv: remove service /etc/init.d/../rc6.d/K06rpcbind insserv: remove service /etc/init.d/../rcS.d/S13rpcbind insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop STDERR "priorities" => { "0"=>[:stop, "06"], "1"=>[:stop, "06"], "6"=>[:stop, "06"], "S"=>[:start, "13"] } }, "not linked" => { "stdout" => "", "stderr" => "insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop" } } }.each do |model, expected_results| context "on #{model}" do context "when update-rc.d shows init linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) @stdout = StringIO.new(expected_results["linked"]["stdout"]) @stderr = StringIO.new(expected_results["linked"]["stderr"]) @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) end it "says the service is enabled" do expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_truthy end it "stores the 'enabled' state" do allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) expect(@provider.load_current_resource).to equal(@current_resource) expect(@current_resource.enabled).to be_truthy end it "stores the start/stop priorities of the service" do @provider.load_current_resource expect(@provider.current_resource.priority).to eq(expected_results["linked"]["priorities"]) end end context "when update-rc.d shows init isn't linked to rc*.d/" do before do allow(@provider).to receive(:assert_update_rcd_available) @stdout = StringIO.new(expected_results["not linked"]["stdout"]) @stderr = StringIO.new(expected_results["not linked"]["stderr"]) @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) end it "says the service is disabled" do expect(@provider.service_currently_enabled?(@provider.get_priority)).to be_falsey end it "stores the 'disabled' state" do allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) expect(@provider.load_current_resource).to equal(@current_resource) expect(@current_resource.enabled).to be_falsey end end end end end describe "action_enable" do shared_examples_for "the service is up to date" do it "does not enable the service" do expect(@provider).not_to receive(:enable_service) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).not_to be_updated end end shared_examples_for "the service is not up to date" do it "enables the service and sets the resource as updated" do expect(@provider).to receive(:enable_service).and_return(true) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).to be_updated end end context "when the service is disabled" do before do @current_resource.enabled(false) end it_behaves_like "the service is not up to date" end context "when the service is enabled" do before do @current_resource.enabled(true) @current_resource.priority(80) end context "and the service sets no priority" do it_behaves_like "the service is up to date" end context "and the service requests the same priority as is set" do before do @new_resource.priority(80) end it_behaves_like "the service is up to date" end context "and the service requests a different priority than is set" do before do @new_resource.priority(20) end it_behaves_like "the service is not up to date" end end end def expect_commands(provider, commands) commands.each do |command| expect(provider).to receive(:shell_out!).with(command) end end describe "enable_service" do let(:service_name) { @new_resource.service_name } context "when the service doesn't set a priority" do it "calls update-rc.d 'service_name' defaults" do expect_commands(@provider, [ "/usr/sbin/update-rc.d -f #{service_name} remove", "/usr/sbin/update-rc.d #{service_name} defaults" ]) @provider.enable_service end end context "when the service sets a simple priority" do before do @new_resource.priority(75) end it "calls update-rc.d 'service_name' defaults" do expect_commands(@provider, [ "/usr/sbin/update-rc.d -f #{service_name} remove", "/usr/sbin/update-rc.d #{service_name} defaults 75 25" ]) @provider.enable_service end end context "when the service sets complex priorities" do before do @new_resource.priority(2 => [:start, 20], 3 => [:stop, 55]) end it "calls update-rc.d 'service_name' with those priorities" do expect_commands(@provider, [ "/usr/sbin/update-rc.d -f #{service_name} remove", "/usr/sbin/update-rc.d #{service_name} start 20 2 . stop 55 3 . " ]) @provider.enable_service end end end describe "disable_service" do let(:service_name) { @new_resource.service_name } context "when the service doesn't set a priority" do it "calls update-rc.d -f 'service_name' remove + stop with default priority" do expect_commands(@provider, [ "/usr/sbin/update-rc.d -f #{service_name} remove", "/usr/sbin/update-rc.d -f #{service_name} stop 80 2 3 4 5 ." ]) @provider.disable_service end end context "when the service sets a simple priority" do before do @new_resource.priority(75) end it "calls update-rc.d -f 'service_name' remove + stop with the specified priority" do expect_commands(@provider, [ "/usr/sbin/update-rc.d -f #{service_name} remove", "/usr/sbin/update-rc.d -f #{service_name} stop #{100 - @new_resource.priority} 2 3 4 5 ." ]) @provider.disable_service end end end end chef-12.3.0/spec/unit/provider/service/freebsd_service_spec.rb0000644000004100000410000005641012520074675024444 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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' class Chef::Provider::Service::Freebsd public :service_enable_variable_name public :determine_enabled_status! public :determine_current_status! end describe Chef::Provider::Service::Freebsd do let(:node) do node = Chef::Node.new node.automatic_attrs[:command] = {:ps => "ps -ax"} node end let(:new_resource) do new_resource = Chef::Resource::Service.new("apache22") new_resource.pattern("httpd") new_resource.supports({:status => false}) new_resource end let(:current_resource) do current_resource = Chef::Resource::Service.new("apache22") current_resource end let(:provider) do events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) provider = Chef::Provider::Service::Freebsd.new(new_resource,run_context) provider.action = :start provider end before do allow(Chef::Resource::Service).to receive(:new).and_return(current_resource) end def stub_etc_rcd_script allow(::File).to receive(:exist?).and_return(false) expect(::File).to receive(:exist?).with("/etc/rc.d/#{new_resource.service_name}").and_return(true) end def stub_usr_local_rcd_script allow(::File).to receive(:exist?).and_return(false) expect(::File).to receive(:exist?).with("/usr/local/etc/rc.d/#{new_resource.service_name}").and_return(true) end def run_load_current_resource stub_usr_local_rcd_script provider.load_current_resource end describe Chef::Provider::Service::Freebsd, "initialize" do it "should default enabled_state_found to false" do expect(provider.enabled_state_found).to be false end it "should find /usr/local/etc/rc.d init scripts" do stub_usr_local_rcd_script expect(provider.init_command).to eql "/usr/local/etc/rc.d/apache22" end it "should find /etc/rc.d init scripts" do stub_etc_rcd_script expect(provider.init_command).to eql "/etc/rc.d/apache22" end it "should set init_command to nil if it can't find anything" do allow(::File).to receive(:exist?).and_return(false) expect(provider.init_command).to be nil end end describe Chef::Provider::Service::Freebsd, "determine_current_status!" do before do stub_usr_local_rcd_script provider.current_resource = current_resource current_resource.service_name(new_resource.service_name) end context "when a status command has been specified" do let(:status) { double(:stdout => "", :exitstatus => 0) } before do new_resource.status_command("/bin/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do expect(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(status) provider.determine_current_status! end end context "when the service supports status" do let(:status) { double(:stdout => "", :exitstatus => 0) } before do new_resource.supports({:status => true}) end it "should run '/etc/init.d/service_name status'" do expect(provider).to receive(:shell_out).with("/usr/local/etc/rc.d/#{new_resource.service_name} status").and_return(status) provider.determine_current_status! end it "should set running to true if the status command returns 0" do expect(provider).to receive(:shell_out).with("/usr/local/etc/rc.d/#{new_resource.service_name} status").and_return(status) provider.determine_current_status! expect(current_resource.running).to be true end it "should set running to false if the status command returns anything except 0" do expect(provider).to receive(:shell_out).with("/usr/local/etc/rc.d/#{new_resource.service_name} status").and_raise(Mixlib::ShellOut::ShellCommandFailed) provider.determine_current_status! expect(current_resource.running).to be false end end context "when we have a 'ps' attribute" do let(:stdout) do StringIO.new(<<-PS_SAMPLE) 413 ?? Ss 0:02.51 /usr/sbin/syslogd -s 539 ?? Is 0:00.14 /usr/sbin/sshd 545 ?? Ss 0:17.53 sendmail: accepting connections (sendmail) PS_SAMPLE end let(:status) { double(:stdout => stdout, :exitstatus => 0) } before do node.automatic_attrs[:command] = {:ps => "ps -ax"} end it "should shell_out! the node's ps command" do expect(provider).to receive(:shell_out!).with(node[:command][:ps]).and_return(status) provider.determine_current_status! end it "should read stdout of the ps command" do allow(provider).to receive(:shell_out!).and_return(status) expect(stdout).to receive(:each_line).and_return(true) provider.determine_current_status! end context "when the regex matches the output" do let(:stdout) do StringIO.new(<<-PS_SAMPLE) 555 ?? Ss 0:05.16 /usr/sbin/cron -s 9881 ?? Ss 0:06.67 /usr/local/sbin/httpd -DNOHTTPACCEPT PS_SAMPLE end it "should set running to true" do allow(provider).to receive(:shell_out!).and_return(status) provider.determine_current_status! expect(current_resource.running).to be_truthy end end it "should set running to false if the regex doesn't match" do allow(provider).to receive(:shell_out!).and_return(status) provider.determine_current_status! expect(current_resource.running).to be_falsey end it "should set running to nil if ps fails" do allow(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) provider.determine_current_status! expect(current_resource.running).to be_nil expect(provider.status_load_success).to be_nil end context "when ps command is nil" do before do node.automatic_attrs[:command] = {:ps => nil} end it "should set running to nil" do pending "superclass raises no conversion of nil to string which seems broken" provider.determine_current_status! expect(current_resource.running).to be_nil end end context "when ps is empty string" do before do node.automatic_attrs[:command] = {:ps => ""} end it "should set running to nil" do provider.determine_current_status! expect(current_resource.running).to be_nil end end end end describe Chef::Provider::Service::Freebsd, "determine_enabled_status!" do before do stub_usr_local_rcd_script provider.current_resource = current_resource current_resource.service_name(new_resource.service_name) allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end context "when /etc/rc.conf does not exist" do before do expect(::File).to receive(:exist?).with("/etc/rc.conf").and_return(false) end it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when /etc/rc.conf does exist" do before do expect(::File).to receive(:exist?).with("/etc/rc.conf").and_return(true) expect(provider).to receive(:read_rc_conf).and_return(lines) end %w{YES Yes yes yEs YeS}.each do |setting| context "when the enable variable is set to #{setting}" do let(:lines) { [ %Q{#{new_resource.service_name}_enable="#{setting}"} ] } it "sets enabled to true" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end end %w{No NO no nO None NONE none nOnE}.each do |setting| context "when the enable variable is set to #{setting}" do let(:lines) { [ %Q{#{new_resource.service_name}_enable="#{setting}"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end end context "when the enable variable is garbage" do let(:lines) { [ %Q{#{new_resource.service_name}_enable="alskdjflasdkjflakdfj"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (left) some other service and we are disabled" do let(:lines) { [ %Q{thing_#{new_resource.service_name}_enable="YES"}, %Q{#{new_resource.service_name}_enable="NO"}, ] } it "sets enabled based on the exact match (false)" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (right) some other service and we are disabled" do let(:lines) { [ %Q{#{new_resource.service_name}_thing_enable="YES"}, %Q{#{new_resource.service_name}_enable="NO"}, ] } it "sets enabled based on the exact match (false)" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (left) some other disabled service and we are enabled" do let(:lines) { [ %Q{thing_#{new_resource.service_name}_enable="NO"}, %Q{#{new_resource.service_name}_enable="YES"}, ] } it "sets enabled based on the exact match (true)" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end context "when the enable variable partial matches (right) some other disabled service and we are enabled" do let(:lines) { [ %Q{#{new_resource.service_name}_thing_enable="NO"}, %Q{#{new_resource.service_name}_enable="YES"}, ] } it "sets enabled based on the exact match (true)" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end context "when the enable variable only partial matches (left) some other enabled service" do let(:lines) { [ %Q{thing_#{new_resource.service_name}_enable="YES"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable only partial matches (right) some other enabled service" do let(:lines) { [ %Q{#{new_resource.service_name}_thing_enable="YES"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when nothing matches" do let(:lines) { [] } it "sets enabled to true" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end end end describe Chef::Provider::Service::Freebsd, "service_enable_variable_name" do before do stub_usr_local_rcd_script provider.current_resource = current_resource current_resource.service_name(new_resource.service_name) expect(::File).to receive(:open).with("/usr/local/etc/rc.d/#{new_resource.service_name}").and_yield(rcscript) end context "when the rc script has a 'name' variable" do let(:rcscript) do StringIO.new(<<-EOF) name="#{new_resource.service_name}" rcvar=`set_rcvar` EOF end it "should not raise an exception if the rcscript have a name variable" do expect { provider.service_enable_variable_name }.not_to raise_error end it "should not run rcvar" do expect(provider).not_to receive(:shell_out!) provider.service_enable_variable_name end it "should return the enable variable determined from the rcscript name" do expect(provider.service_enable_variable_name).to eql "#{new_resource.service_name}_enable" end end describe "when the rcscript does not have a name variable" do let(:rcscript) do StringIO.new <<-EOF rcvar=`set_rcvar` EOF end before do status = double(:stdout => rcvar_stdout, :exitstatus => 0) allow(provider).to receive(:shell_out!).with("/usr/local/etc/rc.d/#{new_resource.service_name} rcvar").and_return(status) end describe "when rcvar returns foobar_enable" do let(:rcvar_stdout) do rcvar_stdout = <<-EOF # apache22 # # #{new_resource.service_name}_enable="YES" # (default: "") EOF end it "should get the service name from rcvar if the rcscript does not have a name variable" do expect(provider.service_enable_variable_name).to eq("#{new_resource.service_name}_enable") end it "should not raise an exception if the rcscript does not have a name variable" do expect { provider.service_enable_variable_name }.not_to raise_error end end describe "when rcvar does not return foobar_enable" do let(:rcvar_stdout) do rcvar_stdout = <<-EOF # service_with_noname # EOF end it "should return nil" do expect(provider.service_enable_variable_name).to be nil end end end end describe Chef::Provider::Service::Freebsd, "load_current_resource" do before(:each) do stub_usr_local_rcd_script expect(provider).to receive(:determine_current_status!) current_resource.running(false) allow(provider).to receive(:service_enable_variable_name).and_return "#{new_resource.service_name}_enable" end it "should create a current resource with the name of the new resource" do expect(Chef::Resource::Service).to receive(:new).and_return(current_resource) provider.load_current_resource end it "should set the current resources service name to the new resources service name" do provider.load_current_resource expect(current_resource.service_name).to eq(new_resource.service_name) end it "should return the current resource" do expect(provider.load_current_resource).to eql(current_resource) end end context "when testing actions" do before(:each) do stub_usr_local_rcd_script expect(provider).to receive(:determine_current_status!) current_resource.running(false) expect(provider).to receive(:determine_enabled_status!) current_resource.enabled(false) provider.load_current_resource end describe Chef::Provider::Service::Freebsd, "start_service" do it "should call the start command if one is specified" do new_resource.start_command("/etc/rc.d/chef startyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally") provider.start_service() end it "should call '/usr/local/etc/rc.d/service_name faststart' if no start command is specified" do expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststart") provider.start_service() end end describe Chef::Provider::Service::Freebsd, "stop_service" do it "should call the stop command if one is specified" do new_resource.stop_command("/etc/init.d/chef itoldyoutostop") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop") provider.stop_service() end it "should call '/usr/local/etc/rc.d/service_name faststop' if no stop command is specified" do expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} faststop") provider.stop_service() end end describe Chef::Provider::Service::Freebsd, "restart_service" do it "should call 'restart' on the service_name if the resource supports it" do new_resource.supports({:restart => true}) expect(provider).to receive(:shell_out_with_systems_locale!).with("/usr/local/etc/rc.d/#{new_resource.service_name} fastrestart") provider.restart_service() end it "should call the restart_command if one has been specified" do new_resource.restart_command("/etc/init.d/chef restartinafire") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire") provider.restart_service() end it "otherwise it should call stop and start" do expect(provider).to receive(:stop_service) expect(provider).to receive(:start_service) provider.restart_service() end end end describe Chef::Provider::Service::Freebsd, "define_resource_requirements" do before do provider.current_resource = current_resource end context "when the init script is not found" do before do provider.init_command = nil allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end [ "start", "reload", "restart", "enable" ].each do |action| it "should raise an exception when the action is #{action}" do provider.define_resource_requirements provider.action = action expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end [ "stop", "disable" ].each do |action| it "should not raise an error when the action is #{action}" do provider.define_resource_requirements provider.action = action expect { provider.process_resource_requirements }.not_to raise_error end end end context "when the init script is found, but the service_enable_variable_name is nil" do before do provider.init_command = nil allow(provider).to receive(:service_enable_variable_name).and_return(nil) end [ "start", "reload", "restart", "enable" ].each do |action| it "should raise an exception when the action is #{action}" do provider.action = action provider.define_resource_requirements expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end [ "stop", "disable" ].each do |action| it "should not raise an error when the action is #{action}" do provider.action = action provider.define_resource_requirements expect { provider.process_resource_requirements }.not_to raise_error end end end end describe Chef::Provider::Service::Freebsd, "enable_service" do before do provider.current_resource = current_resource allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end it "should enable the service if it is not enabled" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "#{new_resource.service_name}_enable=\"NO\"", "bar" ]) expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""]) provider.enable_service() end it "should not partial match an already enabled service" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "thing_#{new_resource.service_name}_enable=\"NO\"", "bar" ]) expect(provider).to receive(:write_rc_conf).with(["foo", "thing_#{new_resource.service_name}_enable=\"NO\"", "bar", "#{new_resource.service_name}_enable=\"YES\""]) provider.enable_service() end it "should enable the service if it is not enabled and not already specified in the rc.conf file" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "bar" ]) expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""]) provider.enable_service() end it "should not enable the service if it is already enabled" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).not_to receive(:write_rc_conf) provider.enable_service end it "should remove commented out versions of it being enabled" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "bar", "\# #{new_resource.service_name}_enable=\"YES\"", "\# #{new_resource.service_name}_enable=\"NO\""]) expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"YES\""]) provider.enable_service() end end describe Chef::Provider::Service::Freebsd, "disable_service" do before do provider.current_resource = current_resource allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end it "should disable the service if it is not disabled" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "#{new_resource.service_name}_enable=\"YES\"", "bar" ]) expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"NO\""]) provider.disable_service() end it "should not disable an enabled service that partially matches" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "thing_#{new_resource.service_name}_enable=\"YES\"", "bar" ]) expect(provider).to receive(:write_rc_conf).with(["foo", "thing_#{new_resource.service_name}_enable=\"YES\"", "bar", "#{new_resource.service_name}_enable=\"NO\""]) provider.disable_service() end it "should not disable the service if it is already disabled" do allow(current_resource).to receive(:enabled).and_return(false) expect(provider).not_to receive(:write_rc_conf) provider.disable_service() end it "should remove commented out versions of it being disabled or enabled" do allow(current_resource).to receive(:enabled).and_return(true) expect(provider).to receive(:read_rc_conf).and_return([ "foo", "bar", "\# #{new_resource.service_name}_enable=\"YES\"", "\# #{new_resource.service_name}_enable=\"NO\""]) expect(provider).to receive(:write_rc_conf).with(["foo", "bar", "#{new_resource.service_name}_enable=\"NO\""]) provider.disable_service() end end end chef-12.3.0/spec/unit/provider/service/openbsd_service_spec.rb0000644000004100000410000005005112520074675024457 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Scott Bonds (scott@ggr.com) # Copyright:: Copyright (c) 2009 Bryan McLellan # Copyright:: Copyright (c) 2014 Scott Bonds # 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' class Chef::Provider::Service::Openbsd public :builtin_service_enable_variable_name public :determine_enabled_status! public :determine_current_status! public :is_enabled? attr_accessor :rc_conf, :rc_conf_local end describe Chef::Provider::Service::Openbsd do let(:node) do node = Chef::Node.new node.automatic_attrs[:command] = {:ps => "ps -ax"} node end let(:new_resource) do new_resource = Chef::Resource::Service.new("sndiod") new_resource.pattern("sndiod") new_resource.supports({:status => false}) new_resource end let(:current_resource) do current_resource = Chef::Resource::Service.new("sndiod") current_resource end let(:provider) do events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) allow(::File).to receive(:read).with('/etc/rc.conf').and_return('') allow(::File).to receive(:read).with('/etc/rc.conf.local').and_return('') provider = Chef::Provider::Service::Openbsd.new(new_resource,run_context) provider.action = :start provider end before do allow(Chef::Resource::Service).to receive(:new).and_return(current_resource) end def stub_etc_rcd_script allow(::File).to receive(:exist?).and_return(false) expect(::File).to receive(:exist?).with("/etc/rc.d/#{new_resource.service_name}").and_return(true) end def run_load_current_resource stub_etc_rcd_script provider.load_current_resource end describe Chef::Provider::Service::Openbsd, "initialize" do it "should find /etc/rc.d init scripts" do stub_etc_rcd_script expect(provider.init_command).to eql "/etc/rc.d/sndiod" end it "should set init_command to nil if it can't find anything" do expect(::File).to receive(:exist?).with('/etc/rc.d/sndiod').and_return(false) expect(provider.init_command).to be nil end end describe Chef::Provider::Service::Openbsd, "determine_current_status!" do before do stub_etc_rcd_script provider.current_resource = current_resource current_resource.service_name(new_resource.service_name) end context "when a status command has been specified" do let(:status) { double(:stdout => "", :exitstatus => 0) } before do new_resource.status_command("/bin/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do expect(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(status) provider.determine_current_status! end end context "when the service supports status" do let(:status) { double(:stdout => "", :exitstatus => 0) } before do new_resource.supports({:status => true}) end it "should run '/etc/rc.d/service_name status'" do expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status) provider.determine_current_status! end it "should set running to true if the status command returns 0" do expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status) provider.determine_current_status! expect(current_resource.running).to be true end it "should set running to false if the status command returns anything except 0" do expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_raise(Mixlib::ShellOut::ShellCommandFailed) provider.determine_current_status! expect(current_resource.running).to be false end end end describe Chef::Provider::Service::Openbsd, "determine_enabled_status!" do before do stub_etc_rcd_script provider.current_resource = current_resource current_resource.service_name(new_resource.service_name) allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end context "when the service is builtin" do before do expect(::File).to receive(:open).with("/etc/rc.d/#{new_resource.service_name}") provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO" provider.rc_conf_local = lines.join("\n") end %w{YES Yes yes yEs YeS}.each do |setting| context "when the enable variable is set to #{setting}" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] } it "sets enabled to true" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end end %w{No NO no nO None NONE none nOnE}.each do |setting| context "when the enable variable is set to #{setting}" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end end context "when the enable variable is garbage" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_enable="alskdjflasdkjflakdfj"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (left) some other service and we are disabled" do let(:lines) { [ %Q{thing_#{provider.builtin_service_enable_variable_name}="YES"}, %Q{#{provider.builtin_service_enable_variable_name}="NO"}, ] } it "sets enabled based on the exact match (false)" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (right) some other service and we are disabled" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_thing="YES"}, %Q{#{provider.builtin_service_enable_variable_name}}, ] } it "sets enabled based on the exact match (false)" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable partial matches (left) some other disabled service and we are enabled" do let(:lines) { [ %Q{thing_#{provider.builtin_service_enable_variable_name}="NO"}, %Q{#{provider.builtin_service_enable_variable_name}="YES"}, ] } it "sets enabled based on the exact match (true)" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end context "when the enable variable partial matches (right) some other disabled service and we are enabled" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_thing="NO"}, %Q{#{provider.builtin_service_enable_variable_name}="YES"}, ] } it "sets enabled based on the exact match (true)" do provider.determine_enabled_status! expect(current_resource.enabled).to be true end end context "when the enable variable only partial matches (left) some other enabled service" do let(:lines) { [ %Q{thing_#{provider.builtin_service_enable_variable_name}_enable="YES"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when the enable variable only partial matches (right) some other enabled service" do let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_thing_enable="YES"} ] } it "sets enabled to false" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end context "when nothing matches" do let(:lines) { [] } it "sets enabled to true" do provider.determine_enabled_status! expect(current_resource.enabled).to be false end end end end describe Chef::Provider::Service::Openbsd, "load_current_resource" do before(:each) do stub_etc_rcd_script expect(provider).to receive(:determine_current_status!) current_resource.running(false) allow(provider).to receive(:service_enable_variable_name).and_return "#{new_resource.service_name}_enable" expect(::File).to receive(:open).with("/etc/rc.d/#{new_resource.service_name}") end it "should create a current resource with the name of the new resource" do expect(Chef::Resource::Service).to receive(:new).and_return(current_resource) provider.load_current_resource end it "should set the current resources service name to the new resources service name" do provider.load_current_resource expect(current_resource.service_name).to eq(new_resource.service_name) end it "should return the current resource" do expect(provider.load_current_resource).to eql(current_resource) end end context "when testing actions" do before(:each) do stub_etc_rcd_script expect(provider).to receive(:determine_current_status!) current_resource.running(false) expect(provider).to receive(:determine_enabled_status!) current_resource.enabled(false) provider.load_current_resource end describe Chef::Provider::Service::Openbsd, "start_service" do it "should call the start command if one is specified" do new_resource.start_command("/etc/rc.d/chef startyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally") provider.start_service() end it "should call '/usr/local/etc/rc.d/service_name start' if no start command is specified" do expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} start") provider.start_service() end end describe Chef::Provider::Service::Openbsd, "stop_service" do it "should call the stop command if one is specified" do new_resource.stop_command("/etc/init.d/chef itoldyoutostop") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop") provider.stop_service() end it "should call '/usr/local/etc/rc.d/service_name stop' if no stop command is specified" do expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} stop") provider.stop_service() end end describe Chef::Provider::Service::Openbsd, "restart_service" do it "should call 'restart' on the service_name if the resource supports it" do new_resource.supports({:restart => true}) expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart") provider.restart_service() end it "should call the restart_command if one has been specified" do new_resource.restart_command("/etc/init.d/chef restartinafire") expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire") provider.restart_service() end it "otherwise it should call stop and start" do expect(provider).to receive(:stop_service) expect(provider).to receive(:start_service) provider.restart_service() end end end describe Chef::Provider::Service::Openbsd, "define_resource_requirements" do before do provider.current_resource = current_resource end context "when the init script is not found" do before do provider.init_command = nil allow(provider).to receive(:builtin_service_enable_variable_name).and_return("#{new_resource.service_name}_enable") end [ "start", "reload", "restart", "enable" ].each do |action| it "should raise an exception when the action is #{action}" do provider.define_resource_requirements provider.action = action expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end [ "stop", "disable" ].each do |action| it "should not raise an error when the action is #{action}" do provider.define_resource_requirements provider.action = action expect { provider.process_resource_requirements }.not_to raise_error end end end context "when the init script is found, but the service_enable_variable_name is nil" do before do allow(provider).to receive(:builtin_service_enable_variable_name).and_return(nil) end [ "start", "reload", "restart", "enable" ].each do |action| it "should raise an exception when the action is #{action}" do provider.action = action provider.define_resource_requirements expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end [ "stop", "disable" ].each do |action| it "should not raise an error when the action is #{action}" do provider.action = action provider.define_resource_requirements expect { provider.process_resource_requirements }.not_to raise_error end end end end describe Chef::Provider::Service::Openbsd, "enable_service" do before do provider.current_resource = current_resource allow(FileUtils).to receive(:touch).with('/etc/rc.conf.local') end context "is builtin and disabled by default" do before do provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO" end context "is enabled" do before do provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=\"\"" end it "should not change rc.conf.local since it is already enabled" do expect(::File).not_to receive(:write) provider.enable_service end end context "is disabled" do before do provider.rc_conf_local = '' end it "should enable the service by adding a line to rc.conf.local" do expect(::File).to receive(:write).with('/etc/rc.conf.local', include("#{provider.builtin_service_enable_variable_name}=\"\"")) expect(provider.is_enabled?).to be false provider.enable_service expect(provider.is_enabled?).to be true end end end context "is builtin and enabled by default" do before do provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=\"\"" end context "is enabled" do before do provider.rc_conf_local = '' end it "should not change rc.conf.local since it is already enabled" do expect(::File).not_to receive(:write) provider.enable_service end end context "is disabled" do before do provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=NO" end it "should enable the service by removing a line from rc.conf.local" do expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{provider.builtin_service_enable_variable_name})$/) expect(provider.is_enabled?).to be false provider.enable_service expect(provider.is_enabled?).to be true end end end context "is not builtin" do before do provider.rc_conf = '' end context "is enabled" do before do provider.rc_conf_local = "pkg_scripts=\"#{new_resource.service_name}\"\n" end it "should not change rc.conf.local since it is already enabled" do expect(::File).not_to receive(:write) provider.enable_service end end context "is disabled" do before do provider.rc_conf_local = '' end it "should enable the service by adding it to the pkg_scripts list" do expect(::File).to receive(:write).with('/etc/rc.conf.local', "\npkg_scripts=\"#{new_resource.service_name}\"\n") expect(provider.is_enabled?).to be false provider.enable_service expect(provider.is_enabled?).to be true end end end end describe Chef::Provider::Service::Openbsd, "disable_service" do before do provider.current_resource = current_resource allow(FileUtils).to receive(:touch).with('/etc/rc.conf.local') end context "is builtin and disabled by default" do before do provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO" end context "is enabled" do before do provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=\"\"" end it "should disable the service by removing its line from rc.conf.local" do expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{provider.builtin_service_enable_variable_name})$/) expect(provider.is_enabled?).to be true provider.disable_service expect(provider.is_enabled?).to be false end end context "is disabled" do before do provider.rc_conf_local = '' end it "should not change rc.conf.local since it is already disabled" do expect(::File).not_to receive(:write) provider.disable_service end end end context "is builtin and enabled by default" do before do provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=\"\"" end context "is enabled" do before do provider.rc_conf_local = '' end it "should disable the service by adding a line to rc.conf.local" do expect(::File).to receive(:write).with('/etc/rc.conf.local', include("#{provider.builtin_service_enable_variable_name}=\"NO\"")) expect(provider.is_enabled?).to be true provider.disable_service expect(provider.is_enabled?).to be false end end context "is disabled" do before do provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=NO" end it "should not change rc.conf.local since it is already disabled" do expect(::File).not_to receive(:write) provider.disable_service end end end context "is not builtin" do before do provider.rc_conf = '' end context "is enabled" do before do provider.rc_conf_local = "pkg_scripts=\"#{new_resource.service_name}\"\n" end it "should disable the service by removing it from the pkg_scripts list" do expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{new_resource.service_name})$/) expect(provider.is_enabled?).to be true provider.disable_service expect(provider.is_enabled?).to be false end end context "is disabled" do before do provider.rc_conf_local = '' end it "should not change rc.conf.local since it is already disabled" do expect(::File).not_to receive(:write) provider.disable_service end end end end end chef-12.3.0/spec/unit/provider/service/windows_spec.rb0000644000004100000410000005025612520074675023006 0ustar www-datawww-data# # Author:: Nuo Yan # Author:: Seth Chisamore # Copyright:: Copyright (c) 2010-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 'spec_helper' require 'mixlib/shellout' describe Chef::Provider::Service::Windows, "load_current_resource" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::WindowsService.new("chef") @provider = Chef::Provider::Service::Windows.new(@new_resource, @run_context) @provider.current_resource = Chef::Resource::WindowsService.new("current-chef") Object.send(:remove_const, 'Win32') if defined?(Win32) Win32 = Module.new Win32::Service = Class.new Win32::Service::AUTO_START = 0x00000002 Win32::Service::DEMAND_START = 0x00000003 Win32::Service::DISABLED = 0x00000004 allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "running")) allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "auto start")) allow(Win32::Service).to receive(:exists?).and_return(true) allow(Win32::Service).to receive(:configure).and_return(Win32::Service) end it "should set the current resources service name to the new resources service name" do @provider.load_current_resource expect(@provider.current_resource.service_name).to eq('chef') end it "should return the current resource" do expect(@provider.load_current_resource).to equal(@provider.current_resource) end it "should set the current resources status" do @provider.load_current_resource expect(@provider.current_resource.running).to be_truthy end it "should set the current resources start type" do @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end it "does not set the current resources start type if it is neither AUTO START or DISABLED" do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "manual")) @provider.load_current_resource expect(@provider.current_resource.enabled).to be_nil end describe Chef::Provider::Service::Windows, "start_service" do before(:each) do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "stopped"), double("StatusStruct", :current_state => "running")) end it "should call the start command if one is specified" do @new_resource.start_command "sc start chef" expect(@provider).to receive(:shell_out!).with("#{@new_resource.start_command}").and_return("Starting custom service") @provider.start_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should use the built-in command if no start command is specified" do expect(Win32::Service).to receive(:start).with(@new_resource.service_name) @provider.start_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should do nothing if the service does not exist" do allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false) expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) @provider.start_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should do nothing if the service is running" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "running")) @provider.load_current_resource expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) @provider.start_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should raise an error if the service is paused" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "paused")) @provider.load_current_resource expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) expect { @provider.start_service }.to raise_error( Chef::Exceptions::Service ) expect(@new_resource.updated_by_last_action?).to be_falsey end it "should wait and continue if the service is in start_pending" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "start pending"), double("StatusStruct", :current_state => "start pending"), double("StatusStruct", :current_state => "running")) @provider.load_current_resource expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) @provider.start_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should fail if the service is in stop_pending" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "stop pending")) @provider.load_current_resource expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) expect { @provider.start_service }.to raise_error( Chef::Exceptions::Service ) expect(@new_resource.updated_by_last_action?).to be_falsey end describe "running as a different account" do let(:old_run_as_user) { @new_resource.run_as_user } let(:old_run_as_password) { @new_resource.run_as_password } before { @new_resource.run_as_user(".\\wallace") @new_resource.run_as_password("Wensleydale") } after { @new_resource.run_as_user(old_run_as_user) @new_resource.run_as_password(old_run_as_password) } it "should call #grant_service_logon if the :run_as_user and :run_as_password attributes are present" do expect(Win32::Service).to receive(:start) expect(@provider).to receive(:grant_service_logon).and_return(true) @provider.start_service end end end describe Chef::Provider::Service::Windows, "stop_service" do before(:each) do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "running"), double("StatusStruct", :current_state => "stopped")) end it "should call the stop command if one is specified" do @new_resource.stop_command "sc stop chef" expect(@provider).to receive(:shell_out!).with("#{@new_resource.stop_command}").and_return("Stopping custom service") @provider.stop_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should use the built-in command if no stop command is specified" do expect(Win32::Service).to receive(:stop).with(@new_resource.service_name) @provider.stop_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should do nothing if the service does not exist" do allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false) expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name) @provider.stop_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should do nothing if the service is stopped" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "stopped")) @provider.load_current_resource expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name) @provider.stop_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should raise an error if the service is paused" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "paused")) @provider.load_current_resource expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) expect { @provider.stop_service }.to raise_error( Chef::Exceptions::Service ) expect(@new_resource.updated_by_last_action?).to be_falsey end it "should wait and continue if the service is in stop_pending" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "stop pending"), double("StatusStruct", :current_state => "stop pending"), double("StatusStruct", :current_state => "stopped")) @provider.load_current_resource expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name) @provider.stop_service expect(@new_resource.updated_by_last_action?).to be_falsey end it "should fail if the service is in start_pending" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "start pending")) @provider.load_current_resource expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name) expect { @provider.stop_service }.to raise_error( Chef::Exceptions::Service ) expect(@new_resource.updated_by_last_action?).to be_falsey end it "should pass custom timeout to the stop command if provided" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "running")) @new_resource.timeout 1 expect(Win32::Service).to receive(:stop).with(@new_resource.service_name) Timeout.timeout(2) do expect { @provider.stop_service }.to raise_error(Timeout::Error) end expect(@new_resource.updated_by_last_action?).to be_falsey end end describe Chef::Provider::Service::Windows, "restart_service" do it "should call the restart command if one is specified" do @new_resource.restart_command "sc restart" expect(@provider).to receive(:shell_out!).with("#{@new_resource.restart_command}") @provider.restart_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should stop then start the service if it is running" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "running"), double("StatusStruct", :current_state => "stopped"), double("StatusStruct", :current_state => "stopped"), double("StatusStruct", :current_state => "running")) expect(Win32::Service).to receive(:stop).with(@new_resource.service_name) expect(Win32::Service).to receive(:start).with(@new_resource.service_name) @provider.restart_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should just start the service if it is stopped" do allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return( double("StatusStruct", :current_state => "stopped"), double("StatusStruct", :current_state => "stopped"), double("StatusStruct", :current_state => "running")) expect(Win32::Service).to receive(:start).with(@new_resource.service_name) @provider.restart_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should do nothing if the service does not exist" do allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false) expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name) expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name) @provider.restart_service expect(@new_resource.updated_by_last_action?).to be_falsey end end describe Chef::Provider::Service::Windows, "enable_service" do before(:each) do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "disabled")) end it "should enable service" do expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::AUTO_START) @provider.enable_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should do nothing if the service does not exist" do allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false) expect(Win32::Service).not_to receive(:configure) @provider.enable_service expect(@new_resource.updated_by_last_action?).to be_falsey end end describe Chef::Provider::Service::Windows, "action_enable" do it "does nothing if the service is enabled" do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "auto start")) expect(@provider).not_to receive(:enable_service) @provider.action_enable end it "enables the service if it is not set to automatic start" do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "disabled")) expect(@provider).to receive(:enable_service) @provider.action_enable end end describe Chef::Provider::Service::Windows, "action_disable" do it "does nothing if the service is disabled" do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "disabled")) expect(@provider).not_to receive(:disable_service) @provider.action_disable end it "disables the service if it is not set to disabled" do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "auto start")) expect(@provider).to receive(:disable_service) @provider.action_disable end end describe Chef::Provider::Service::Windows, "disable_service" do before(:each) do allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return( double("ConfigStruct", :start_type => "auto start")) end it "should disable service" do expect(Win32::Service).to receive(:configure) @provider.disable_service expect(@new_resource.updated_by_last_action?).to be_truthy end it "should do nothing if the service does not exist" do allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false) expect(Win32::Service).not_to receive(:configure) @provider.disable_service expect(@new_resource.updated_by_last_action?).to be_falsey end end describe Chef::Provider::Service::Windows, "action_configure_startup" do { :automatic => "auto start", :manual => "demand start", :disabled => "disabled" }.each do |type,win32| it "sets the startup type to #{type} if it is something else" do @new_resource.startup_type(type) allow(@provider).to receive(:current_start_type).and_return("fire") expect(@provider).to receive(:set_startup_type).with(type) @provider.action_configure_startup end it "leaves the startup type as #{type} if it is already set" do @new_resource.startup_type(type) allow(@provider).to receive(:current_start_type).and_return(win32) expect(@provider).not_to receive(:set_startup_type).with(type) @provider.action_configure_startup end end end describe Chef::Provider::Service::Windows, "set_start_type" do it "when called with :automatic it calls Win32::Service#configure with Win32::Service::AUTO_START" do expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::AUTO_START) @provider.send(:set_startup_type, :automatic) end it "when called with :manual it calls Win32::Service#configure with Win32::Service::DEMAND_START" do expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::DEMAND_START) @provider.send(:set_startup_type, :manual) end it "when called with :disabled it calls Win32::Service#configure with Win32::Service::DISABLED" do expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::DISABLED) @provider.send(:set_startup_type, :disabled) end it "raises an exception when given an unknown start type" do expect { @provider.send(:set_startup_type, :fire_truck) }.to raise_error(Chef::Exceptions::ConfigurationError) end end shared_context "testing private methods" do let(:private_methods) { described_class.private_instance_methods } before { described_class.send(:public, *private_methods) } after { described_class.send(:private, *private_methods) } end describe "grant_service_logon" do include_context "testing private methods" let(:username) { "unit_test_user" } let(:success_string) { "The task has completed successfully.\r\nSee logfile etc." } let(:failure_string) { "Look on my works, ye Mighty, and despair!" } let(:command) { dbfile = @provider.grant_dbfile_name(username) policyfile = @provider.grant_policyfile_name(username) logfile = @provider.grant_logfile_name(username) %Q{secedit.exe /configure /db "#{dbfile}" /cfg "#{policyfile}" /areas USER_RIGHTS SECURITYPOLICY SERVICES /log "#{logfile}"} } let(:shellout_env) { {:environment=>{"LC_ALL"=>"en_US.UTF-8"}} } before { expect_any_instance_of(described_class).to receive(:shell_out).with(command).and_call_original expect_any_instance_of(Mixlib::ShellOut).to receive(:run_command).and_return(nil) } after { # only needed for the second test. ::File.delete(@provider.grant_policyfile_name(username)) rescue nil ::File.delete(@provider.grant_logfile_name(username)) rescue nil ::File.delete(@provider.grant_dbfile_name(username)) rescue nil } it "calls Mixlib::Shellout with the correct command string" do expect_any_instance_of(Mixlib::ShellOut).to receive(:exitstatus).and_return(0) expect(@provider.grant_service_logon(username)).to equal true end it "raises an exception when the grant command fails" do expect_any_instance_of(Mixlib::ShellOut).to receive(:exitstatus).and_return(1) expect_any_instance_of(Mixlib::ShellOut).to receive(:stdout).and_return(failure_string) expect { @provider.grant_service_logon(username) }.to raise_error(Chef::Exceptions::Service) end end describe "cleaning usernames" do include_context "testing private methods" it "correctly reformats usernames to create valid filenames" do expect(@provider.clean_username_for_path("\\\\problem username/oink.txt")).to eq("_problem_username_oink_txt") expect(@provider.clean_username_for_path("boring_username")).to eq("boring_username") end it "correctly reformats usernames for the policy file" do expect(@provider.canonicalize_username(".\\maryann")).to eq("maryann") expect(@provider.canonicalize_username("maryann")).to eq("maryann") expect(@provider.canonicalize_username("\\\\maryann")).to eq("maryann") expect(@provider.canonicalize_username("mydomain\\\\maryann")).to eq("mydomain\\\\maryann") expect(@provider.canonicalize_username("\\\\mydomain\\\\maryann")).to eq("mydomain\\\\maryann") end end end chef-12.3.0/spec/unit/provider/service/gentoo_service_spec.rb0000644000004100000410000001301312520074675024315 0ustar www-datawww-data# # Author:: Lee Jensen () # Author:: AJ Christensen () # 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' describe Chef::Provider::Service::Gentoo do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Gentoo.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out).and_return(@status) allow(File).to receive(:exists?).with("/etc/init.d/chef").and_return(true) allow(File).to receive(:exists?).with("/sbin/rc-update").and_return(true) allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(false) allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(false) end # new test: found_enabled state # describe "load_current_resource" do it "should raise Chef::Exceptions::Service if /sbin/rc-update does not exist" do expect(File).to receive(:exists?).with("/sbin/rc-update").and_return(false) @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should track when service file is not found in /etc/runlevels" do @provider.load_current_resource expect(@provider.instance_variable_get("@found_script")).to be_falsey end it "should track when service file is found in /etc/runlevels/**/" do allow(Dir).to receive(:glob).with("/etc/runlevels/**/chef").and_return(["/etc/runlevels/default/chef"]) @provider.load_current_resource expect(@provider.instance_variable_get("@found_script")).to be_truthy end describe "when detecting the service enable state" do describe "and the glob returns a default service script file" do before do allow(Dir).to receive(:glob).with("/etc/runlevels/**/chef").and_return(["/etc/runlevels/default/chef"]) end describe "and the file exists and is readable" do before do allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(true) allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(true) end it "should set enabled to true" do @provider.load_current_resource expect(@current_resource.enabled).to be_truthy end end describe "and the file exists but is not readable" do before do allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(true) allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return(false) end it "should set enabled to false" do @provider.load_current_resource expect(@current_resource.enabled).to be_falsey end end describe "and the file does not exist" do before do allow(File).to receive(:exists?).with("/etc/runlevels/default/chef").and_return(false) allow(File).to receive(:readable?).with("/etc/runlevels/default/chef").and_return("foobarbaz") end it "should set enabled to false" do @provider.load_current_resource expect(@current_resource.enabled).to be_falsey end end end end it "should return the current_resource" do expect(@provider.load_current_resource).to eq(@current_resource) end it "should support the status command automatically" do @provider.load_current_resource expect(@new_resource.supports[:status]).to be_truthy end it "should support the restart command automatically" do @provider.load_current_resource expect(@new_resource.supports[:restart]).to be_truthy end it "should not support the reload command automatically" do @provider.load_current_resource expect(@new_resource.supports[:reload]).not_to be_truthy end end describe "action_methods" do before(:each) { allow(@provider).to receive(:load_current_resource).and_return(@current_resource) } describe Chef::Provider::Service::Gentoo, "enable_service" do it "should call rc-update add *service* default" do expect(@provider).to receive(:shell_out!).with("/sbin/rc-update add chef default") @provider.enable_service() end end describe Chef::Provider::Service::Gentoo, "disable_service" do it "should call rc-update del *service* default" do expect(@provider).to receive(:shell_out!).with("/sbin/rc-update del chef default") @provider.disable_service() end end end end chef-12.3.0/spec/unit/provider/service/aix_service_spec.rb0000644000004100000410000001406112520074675023607 0ustar www-datawww-data# # Author:: Kaustubh # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Service::Aix do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Aix.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) end describe "load current resource" do it "should create a current resource with the name of the new resource and determine the status" do @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource) expect(@current_resource).to receive(:service_name).with("chef") expect(@provider).to receive(:determine_current_status!) @provider.load_current_resource end end describe "determine current status" do context "when the service is active" do before do @status = double("Status", :exitstatus => 0, :stdout => "chef chef 12345 active\n") end it "current resource is running" do expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status) expect(@provider).to receive(:is_resource_group?).with(["chef chef 12345 active"]) @provider.load_current_resource expect(@current_resource.running).to be_truthy end end context "when the service is inoprative" do before do @status = double("Status", :exitstatus => 0, :stdout => "chef chef inoperative\n") end it "current resource is not running" do expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status) expect(@provider).to receive(:is_resource_group?).with(["chef chef inoperative"]) @provider.load_current_resource expect(@current_resource.running).to be_falsey end end end describe "is resource group" do context "when there are mutiple subsystems associated with group" do before do @status = double("Status", :exitstatus => 0, :stdout => "chef1 chef 12345 active\nchef2 chef 12334 active\nchef3 chef inoperative") end it "service is a group" do expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status) @provider.load_current_resource expect(@provider.instance_eval("@is_resource_group")).to be_truthy end end context "when there is a single subsystem in the group" do before do @status = double("Status", :exitstatus => 0, :stdout => "chef1 chef inoperative\n") end it "service is a group" do expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status) @provider.load_current_resource expect(@provider.instance_eval("@is_resource_group")).to be_truthy end end context "when there service is a subsytem" do before do @status = double("Status", :exitstatus => 0, :stdout => "chef chef123 inoperative\n") end it "service is a subsystem" do expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status) @provider.load_current_resource expect(@provider.instance_eval("@is_resource_group")).to be_falsey end end end describe "when starting the service" do before do @new_resource.service_name "apache" end it "should call the start command for groups" do @provider.instance_eval('@is_resource_group = true') expect(@provider).to receive(:shell_out!).with("startsrc -g #{@new_resource.service_name}") @provider.start_service end it "should call the start command for subsystem" do expect(@provider).to receive(:shell_out!).with("startsrc -s #{@new_resource.service_name}") @provider.start_service end end describe "when stopping a service" do before do @new_resource.service_name "apache" end it "should call the stop command for groups" do @provider.instance_eval('@is_resource_group = true') expect(@provider).to receive(:shell_out!).with("stopsrc -g #{@new_resource.service_name}") @provider.stop_service end it "should call the stop command for subsystem" do expect(@provider).to receive(:shell_out!).with("stopsrc -s #{@new_resource.service_name}") @provider.stop_service end end describe "when reloading a service" do before do @new_resource.service_name "apache" end it "should call the reload command for groups" do @provider.instance_eval('@is_resource_group = true') expect(@provider).to receive(:shell_out!).with("refresh -g #{@new_resource.service_name}") @provider.reload_service end it "should call the reload command for subsystem" do expect(@provider).to receive(:shell_out!).with("refresh -s #{@new_resource.service_name}") @provider.reload_service end end describe "when restarting the service" do it "should call stop service followed by start service" do expect(@provider).to receive(:stop_service) expect(@provider).to receive(:start_service) @provider.restart_service end end end chef-12.3.0/spec/unit/provider/service/arch_service_spec.rb0000644000004100000410000003233512520074675023747 0ustar www-datawww-data# # Author:: Jan Zimmek () # Author:: AJ Christensen () # 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' require 'ostruct' # most of this code has been ripped from init_service_spec.rb # and is only slightly modified to match "arch" needs. describe Chef::Provider::Service::Arch, "load_current_resource" do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => "ps -ef"} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @new_resource.pattern("chef") @new_resource.supports({:status => false}) @provider = Chef::Provider::Service::Arch.new(@new_resource, @run_context) allow(::File).to receive(:exists?).with("/etc/rc.conf").and_return(true) allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network apache sshd)") end describe "when first created" do it "should set the current resources service name to the new resources service name" do allow(@provider).to receive(:shell_out).and_return(OpenStruct.new(:exitstatus => 0, :stdout => "")) @provider.load_current_resource expect(@provider.current_resource.service_name).to eq('chef') end end describe "when the service supports status" do before do @new_resource.supports({:status => true}) end it "should run '/etc/rc.d/service_name status'" do expect(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0)) @provider.load_current_resource end it "should set running to true if the status command returns 0" do allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 0)) @provider.load_current_resource expect(@provider.current_resource.running).to be_truthy end it "should set running to false if the status command returns anything except 0" do allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_return(OpenStruct.new(:exitstatus => 1)) @provider.load_current_resource expect(@provider.current_resource.running).to be_falsey end it "should set running to false if the status command raises" do allow(@provider).to receive(:shell_out).with("/etc/rc.d/chef status").and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.load_current_resource expect(@provider.current_resource.running).to be_falsey end end describe "when a status command has been specified" do before do @new_resource.status_command("/etc/rc.d/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do expect(@provider).to receive(:shell_out).with("/etc/rc.d/chefhasmonkeypants status").and_return(OpenStruct.new(:exitstatus => 0)) @provider.load_current_resource end end it "should raise error if the node has a nil ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => nil} @provider.define_resource_requirements @provider.action = :start expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should raise error if the node has an empty ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => ""} @provider.define_resource_requirements @provider.action = :start expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should fail if file /etc/rc.conf does not exist" do allow(::File).to receive(:exists?).with("/etc/rc.conf").and_return(false) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service) end it "should fail if file /etc/rc.conf does not contain DAEMONS array" do allow(::File).to receive(:read).with("/etc/rc.conf").and_return("") expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service) end describe "when discovering service status with ps" do before do @stdout = StringIO.new(<<-DEFAULT_PS) aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb DEFAULT_PS @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) @node.automatic_attrs[:command] = {:ps => "ps -ef"} end it "determines the service is running when it appears in ps" do @stdout = StringIO.new(<<-RUNNING_PS) aj 7842 5057 0 21:26 pts/2 00:00:06 chef aj 7842 5057 0 21:26 pts/2 00:00:06 poos RUNNING_PS allow(@status).to receive(:stdout).and_return(@stdout) @provider.load_current_resource expect(@provider.current_resource.running).to be_truthy end it "determines the service is not running when it does not appear in ps" do allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@provider.current_resource.running).to be_falsey end it "should raise an exception if ps fails" do allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.load_current_resource @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end it "should return existing entries in DAEMONS array" do allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network !apache ssh)") expect(@provider.daemons).to eq(['network', '!apache', 'ssh']) end context "when the current service status is known" do before do @current_resource = Chef::Resource::Service.new("chef") @provider.current_resource = @current_resource end describe Chef::Provider::Service::Arch, "enable_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:start_command).and_return(false) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should add chef to DAEMONS array" do allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network)") expect(@provider).to receive(:update_daemons).with(['network', 'chef']) @provider.enable_service() end end describe Chef::Provider::Service::Arch, "disable_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:start_command).and_return(false) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should remove chef from DAEMONS array" do allow(::File).to receive(:read).with("/etc/rc.conf").and_return("DAEMONS=(network chef)") expect(@provider).to receive(:update_daemons).with(['network', '!chef']) @provider.disable_service() end end describe Chef::Provider::Service::Arch, "start_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:start_command).and_return(false) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should call the start command if one is specified" do allow(@new_resource).to receive(:start_command).and_return("/etc/rc.d/chef startyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally") @provider.start_service() end it "should call '/etc/rc.d/service_name start' if no start command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} start") @provider.start_service() end end describe Chef::Provider::Service::Arch, "stop_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:stop_command).and_return(false) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should call the stop command if one is specified" do allow(@new_resource).to receive(:stop_command).and_return("/etc/rc.d/chef itoldyoutostop") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef itoldyoutostop") @provider.stop_service() end it "should call '/etc/rc.d/service_name stop' if no stop command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} stop") @provider.stop_service() end end describe Chef::Provider::Service::Arch, "restart_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:restart_command).and_return(false) # @new_resource.stub(:supports).and_return({:restart => false}) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should call 'restart' on the service_name if the resource supports it" do allow(@new_resource).to receive(:supports).and_return({:restart => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restart") @provider.restart_service() end it "should call the restart_command if one has been specified" do allow(@new_resource).to receive(:restart_command).and_return("/etc/rc.d/chef restartinafire") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} restartinafire") @provider.restart_service() end it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do expect(@provider).to receive(:stop_service) expect(@provider).to receive(:sleep).with(1) expect(@provider).to receive(:start_service) @provider.restart_service() end end describe Chef::Provider::Service::Arch, "reload_service" do # before(:each) do # @new_resource = double("Chef::Resource::Service", # :null_object => true, # :name => "chef", # :service_name => "chef", # :running => false # ) # @new_resource.stub(:reload_command).and_return(false) # @new_resource.stub(:supports).and_return({:reload => false}) # # @provider = Chef::Provider::Service::Arch.new(@node, @new_resource) # Chef::Resource::Service.stub(:new).and_return(@current_resource) # end it "should call 'reload' on the service if it supports it" do allow(@new_resource).to receive(:supports).and_return({:reload => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} reload") @provider.reload_service() end it "should should run the user specified reload command if one is specified and the service doesn't support reload" do allow(@new_resource).to receive(:reload_command).and_return("/etc/rc.d/chef lollerpants") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{@new_resource.service_name} lollerpants") @provider.reload_service() end end end end chef-12.3.0/spec/unit/provider/service/upstart_service_spec.rb0000644000004100000410000003353112520074675024533 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2010 Bryan McLellan # 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::Service::Upstart do before(:each) do @node =Chef::Node.new @node.name('upstarter') @node.automatic_attrs[:platform] = 'ubuntu' @node.automatic_attrs[:platform_version] = '9.10' @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("rsyslog") @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) end describe "when first created" do before do @platform = nil end it "should return /etc/event.d as the upstart job directory when running on Ubuntu 9.04" do @node.automatic_attrs[:platform_version] = '9.04' #Chef::Platform.stub(:find_platform_and_version).and_return([ "ubuntu", "9.04" ]) @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) expect(@provider.instance_variable_get(:@upstart_job_dir)).to eq("/etc/event.d") expect(@provider.instance_variable_get(:@upstart_conf_suffix)).to eq("") end it "should return /etc/init as the upstart job directory when running on Ubuntu 9.10" do @node.automatic_attrs[:platform_version] = '9.10' @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) expect(@provider.instance_variable_get(:@upstart_job_dir)).to eq("/etc/init") expect(@provider.instance_variable_get(:@upstart_conf_suffix)).to eq(".conf") end it "should return /etc/init as the upstart job directory by default" do @node.automatic_attrs[:platform_version] = '9000' @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) expect(@provider.instance_variable_get(:@upstart_job_dir)).to eq("/etc/init") expect(@provider.instance_variable_get(:@upstart_conf_suffix)).to eq(".conf") end end describe "load_current_resource" do before(:each) do @node.automatic_attrs[:command] = {:ps => "ps -ax"} @current_resource = Chef::Resource::Service.new("rsyslog") allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @status = double("Status", :exitstatus => 0) allow(@provider).to receive(:popen4).and_return(@status) @stdin = StringIO.new @stdout = StringIO.new @stderr = StringIO.new @pid = double("PID") allow(::File).to receive(:exists?).and_return(true) allow(::File).to receive(:open).and_return(true) end it "should create a current resource with the name of the new resource" do expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources service name to the new resources service name" do expect(@current_resource).to receive(:service_name).with(@new_resource.service_name) @provider.load_current_resource end it "should not change the service name when parameters are specified" do @new_resource.parameters({ "OSD_ID" => "2" }) @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) @provider.current_resource = @current_resource expect(@new_resource.service_name).to eq(@current_resource.service_name) end it "should run '/sbin/status rsyslog'" do expect(@provider).to receive(:popen4).with("/sbin/status rsyslog").and_return(@status) @provider.load_current_resource end describe "when the status command uses the new format" do before do end it "should set running to true if the status command returns 0" do @stdout = StringIO.new("rsyslog start/running") allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the status command returns anything except 0" do @stdout = StringIO.new("rsyslog stop/waiting") allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end end describe "when the status command uses the old format" do it "should set running to true if the status command returns 0" do @stdout = StringIO.new("rsyslog (start) running, process 32225") allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the status command returns anything except 0" do @stdout = StringIO.new("rsyslog (stop) waiting") allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end end it "should set running to false if it catches a Chef::Exceptions::Exec" do allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_raise(Chef::Exceptions::Exec) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end it "should set enabled to true when it finds 'starts on'" do @lines = double("start on filesystem", :gets => "start on filesystem") allow(::File).to receive(:open).and_yield(@lines) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end it "should set enabled to false when it finds '#starts on'" do @lines = double("start on filesystem", :gets => "#start on filesystem") allow(::File).to receive(:open).and_yield(@lines) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end it "should assume disable when no job configuration file is found" do allow(::File).to receive(:exists?).and_return(false) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end it "should track state when the upstart configuration file fails to load" do expect(File).to receive(:exists?).and_return false @provider.load_current_resource expect(@provider.instance_variable_get("@config_file_found")).to eq(false) end describe "when a status command has been specified" do before do allow(@new_resource).to receive(:status_command).and_return("/bin/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do allow(@provider).to receive(:shell_out!).with("/bin/chefhasmonkeypants status").and_return(0) expect(@current_resource).to receive(:running).with(true) @provider.load_current_resource end it "should track state when the user-provided status command fails" do allow(@provider).to receive(:shell_out!).and_raise(Errno::ENOENT) @provider.load_current_resource expect(@provider.instance_variable_get("@command_success")).to eq(false) end it "should set running to false if it catches a Chef::Exceptions::Exec when using a status command" do allow(@provider).to receive(:shell_out!).and_raise(Errno::ENOENT) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end end it "should track state when we fail to obtain service status via upstart_state" do expect(@provider).to receive(:upstart_state).and_raise Chef::Exceptions::Exec @provider.load_current_resource expect(@provider.instance_variable_get("@command_success")).to eq(false) end it "should return the current resource" do expect(@provider.load_current_resource).to eql(@current_resource) end end describe "enable and disable service" do before(:each) do @current_resource = Chef::Resource::Service.new('rsyslog') allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @provider.current_resource = @current_resource allow(Chef::Util::FileEdit).to receive(:new) end it "should enable the service if it is not enabled" do @file = Object.new allow(Chef::Util::FileEdit).to receive(:new).and_return(@file) allow(@current_resource).to receive(:enabled).and_return(false) expect(@file).to receive(:search_file_replace) expect(@file).to receive(:write_file) @provider.enable_service() end it "should disable the service if it is enabled" do @file = Object.new allow(Chef::Util::FileEdit).to receive(:new).and_return(@file) allow(@current_resource).to receive(:enabled).and_return(true) expect(@file).to receive(:search_file_replace) expect(@file).to receive(:write_file) @provider.disable_service() end end describe "start and stop service" do before(:each) do @current_resource = Chef::Resource::Service.new('rsyslog') allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @provider.current_resource = @current_resource end it "should call the start command if one is specified" do allow(@new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally") @provider.start_service() end it "should call '/sbin/start service_name' if no start command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(0) @provider.start_service() end it "should not call '/sbin/start service_name' if it is already running" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).not_to receive(:shell_out_with_systems_locale!) @provider.start_service() end it "should pass parameters to the start command if they are provided" do @new_resource = Chef::Resource::Service.new("rsyslog") @new_resource.parameters({ "OSD_ID" => "2" }) @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context) @provider.current_resource = @current_resource expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start rsyslog OSD_ID=2").and_return(0) @provider.start_service() end it "should call the restart command if one is specified" do allow(@current_resource).to receive(:running).and_return(true) allow(@new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally") @provider.restart_service() end it "should call '/sbin/restart service_name' if no restart command is specified" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/restart #{@new_resource.service_name}").and_return(0) @provider.restart_service() end it "should call '/sbin/start service_name' if restart_service is called for a stopped service" do allow(@current_resource).to receive(:running).and_return(false) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(0) @provider.restart_service() end it "should call the reload command if one is specified" do allow(@current_resource).to receive(:running).and_return(true) allow(@new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally") @provider.reload_service() end it "should call '/sbin/reload service_name' if no reload command is specified" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/reload #{@new_resource.service_name}").and_return(0) @provider.reload_service() end it "should call the stop command if one is specified" do allow(@current_resource).to receive(:running).and_return(true) allow(@new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally") @provider.stop_service() end it "should call '/sbin/stop service_name' if no stop command is specified" do allow(@current_resource).to receive(:running).and_return(true) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}").and_return(0) @provider.stop_service() end it "should not call '/sbin/stop service_name' if it is already stopped" do allow(@current_resource).to receive(:running).and_return(false) expect(@provider).not_to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}") @provider.stop_service() end end end chef-12.3.0/spec/unit/provider/service/insserv_service_spec.rb0000644000004100000410000000530512520074675024520 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'spec_helper' describe Chef::Provider::Service::Insserv do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @node.automatic_attrs[:command] = {:ps => "ps -ax"} @new_resource = Chef::Resource::Service.new("initgrediant") @current_resource = Chef::Resource::Service.new("initgrediant") @provider = Chef::Provider::Service::Insserv.new(@new_resource, @run_context) @status = double("Process::Status mock", :exitstatus => 0, :stdout => "") allow(@provider).to receive(:shell_out!).and_return(@status) end describe "load_current_resource" do describe "when startup links exist" do before do allow(Dir).to receive(:glob).with("/etc/rc**/S*initgrediant").and_return(["/etc/rc5.d/S18initgrediant", "/etc/rc2.d/S18initgrediant", "/etc/rc4.d/S18initgrediant", "/etc/rc3.d/S18initgrediant"]) end it "sets the current enabled status to true" do @provider.load_current_resource expect(@provider.current_resource.enabled).to be_truthy end end describe "when startup links do not exist" do before do allow(Dir).to receive(:glob).with("/etc/rc**/S*initgrediant").and_return([]) end it "sets the current enabled status to false" do @provider.load_current_resource expect(@provider.current_resource.enabled).to be_falsey end end end describe "enable_service" do it "should call insserv and create the default links" do expect(@provider).to receive(:shell_out!).with("/sbin/insserv -r -f #{@new_resource.service_name}") expect(@provider).to receive(:shell_out!).with("/sbin/insserv -d -f #{@new_resource.service_name}") @provider.enable_service end end describe "disable_service" do it "should call insserv and remove the links" do expect(@provider).to receive(:shell_out!).with("/sbin/insserv -r -f #{@new_resource.service_name}") @provider.disable_service end end end chef-12.3.0/spec/unit/provider/service/solaris_smf_service_spec.rb0000644000004100000410000001647712520074675025364 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Copyright:: Copyright (c) 2010 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::Service::Solaris do before(:each) do @node =Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new('chef') @current_resource = Chef::Resource::Service.new('chef') @provider = Chef::Provider::Service::Solaris.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @stdin = StringIO.new @stdout = StringIO.new @stderr = StringIO.new @pid = 2342 @stdout_string = "state disabled" allow(@stdout).to receive(:gets).and_return(@stdout_string) @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) end it "should raise an error if /bin/svcs does not exist" do expect(File).to receive(:exists?).with("/bin/svcs").and_return(false) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service) end describe "on a host with /bin/svcs" do before do allow(File).to receive(:exists?).with('/bin/svcs').and_return(true) end describe "when discovering the current service state" do it "should create a current resource with the name of the new resource" do allow(@provider).to receive(:shell_out!).with("/bin/svcs -l chef").and_return(@status) expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should return the current resource" do allow(@provider).to receive(:shell_out!).with("/bin/svcs -l chef").and_return(@status) expect(@provider.load_current_resource).to eql(@current_resource) end it "should call '/bin/svcs -l service_name'" do expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status) @provider.load_current_resource end it "should mark service as not running" do allow(@provider).to receive(:shell_out!).and_return(@status) expect(@current_resource).to receive(:running).with(false) @provider.load_current_resource end it "should mark service as running" do @status = double("Status", :exitstatus => 0, :stdout => 'state online') allow(@provider).to receive(:shell_out!).and_return(@status) expect(@current_resource).to receive(:running).with(true) @provider.load_current_resource end it "should not mark service as maintenance" do allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@provider.maintenance).to be_falsey end it "should mark service as maintenance" do @status = double("Status", :exitstatus => 0, :stdout => 'state maintenance') allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@provider.maintenance).to be_truthy end end describe "when enabling the service" do before(:each) do @provider.current_resource = @current_resource @current_resource.enabled(true) end it "should call svcadm enable -s chef" do expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}") expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status) expect(@provider.enable_service).to be_truthy expect(@current_resource.enabled).to be_truthy end it "should call svcadm enable -s chef for start_service" do expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}") expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status) expect(@provider.start_service).to be_truthy expect(@current_resource.enabled).to be_truthy end it "should call svcadm clear chef for start_service when state maintenance" do @status = double("Status", :exitstatus => 0, :stdout => 'state maintenance') allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}").and_return(@status) expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status) expect(@provider.enable_service).to be_truthy expect(@current_resource.enabled).to be_truthy end end describe "when disabling the service" do before(:each) do @provider.current_resource = @current_resource @current_resource.enabled(false) end it "should call svcadm disable -s chef" do expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm disable -s chef").and_return(@status) expect(@provider.disable_service).to be_truthy expect(@current_resource.enabled).to be_falsey end it "should call svcadm disable -s chef for stop_service" do expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm disable -s chef").and_return(@status) expect(@provider.stop_service).to be_truthy expect(@current_resource.enabled).to be_falsey end end describe "when reloading the service" do before(:each) do @status = double("Process::Status", :exitstatus => 0) @provider.current_resource = @current_resource end it "should call svcadm refresh chef" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/svcadm refresh chef").and_return(@status) @provider.reload_service end end describe "when the service doesn't exist" do before(:each) do @stdout_string = "" @status = double("Status", :exitstatus => 1, :stdout => @stdout) @provider.current_resource = @current_resource end it "should be marked not running" do expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status) @provider.service_status expect(@current_resource.running).to be_falsey end it "should be marked not enabled" do expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status) @provider.service_status expect(@current_resource.enabled).to be_falsey end end end end chef-12.3.0/spec/unit/provider/service/init_service_spec.rb0000644000004100000410000002247312520074675023777 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Service::Init, "load_current_resource" do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => "ps -ef"} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Init.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @stdout = StringIO.new(<<-PS) aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb PS @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) end it "should create a current resource with the name of the new resource" do @provider.load_current_resource expect(@provider.current_resource).to equal(@current_resource) end it "should set the current resources service name to the new resources service name" do @provider.load_current_resource expect(@current_resource.service_name).to eq('chef') end describe "when the service supports status" do before do @new_resource.supports({:status => true}) end it "should run '/etc/init.d/service_name status'" do expect(@provider).to receive(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource end it "should set running to true if the status command returns 0" do allow(@provider).to receive(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the status command returns anything except 0" do allow(@status).to receive(:exitstatus).and_return(1) allow(@provider).to receive(:shell_out).with("/etc/init.d/#{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end it "should set running to false if the status command raises" do allow(@provider).to receive(:shell_out).and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.load_current_resource expect(@current_resource.running).to be_falsey end end describe "when a status command has been specified" do before do allow(@new_resource).to receive(:status_command).and_return("/etc/init.d/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do expect(@provider).to receive(:shell_out).with("/etc/init.d/chefhasmonkeypants status").and_return(@status) @provider.load_current_resource end end describe "when an init command has been specified" do before do allow(@new_resource).to receive(:init_command).and_return("/opt/chef-server/service/erchef") @provider = Chef::Provider::Service::Init.new(@new_resource, @run_context) end it "should use the init_command if one has been specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/opt/chef-server/service/erchef start") @provider.start_service end end describe "when the node has not specified a ps command" do it "should raise an error if the node has a nil ps attribute" do @node.automatic_attrs[:command] = {:ps => nil} @provider.load_current_resource @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should raise an error if the node has an empty ps attribute" do @node.automatic_attrs[:command] = {:ps => ""} @provider.load_current_resource @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end describe "when we have a 'ps' attribute" do it "should shell_out! the node's ps command" do expect(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource end it "should set running to true if the regex matches the output" do @stdout = StringIO.new(<<-RUNNING_PS) aj 7842 5057 0 21:26 pts/2 00:00:06 chef aj 7842 5057 0 21:26 pts/2 00:00:06 poos RUNNING_PS allow(@status).to receive(:stdout).and_return(@stdout) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the regex doesn't match" do allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end it "should raise an exception if ps fails" do allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.load_current_resource @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end it "should return the current resource" do expect(@provider.load_current_resource).to eql(@current_resource) end describe "when starting the service" do it "should call the start command if one is specified" do @new_resource.start_command("/etc/init.d/chef startyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef startyousillysally") @provider.start_service() end it "should call '/etc/init.d/service_name start' if no start command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} start") @provider.start_service() end end describe Chef::Provider::Service::Init, "stop_service" do it "should call the stop command if one is specified" do @new_resource.stop_command("/etc/init.d/chef itoldyoutostop") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop") @provider.stop_service() end it "should call '/etc/init.d/service_name stop' if no stop command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} stop") @provider.stop_service() end end describe "when restarting a service" do it "should call 'restart' on the service_name if the resource supports it" do @new_resource.supports({:restart => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} restart") @provider.restart_service() end it "should call the restart_command if one has been specified" do @new_resource.restart_command("/etc/init.d/chef restartinafire") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/#{@new_resource.service_name} restartinafire") @provider.restart_service() end it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do expect(@provider).to receive(:stop_service) expect(@provider).to receive(:sleep).with(1) expect(@provider).to receive(:start_service) @provider.restart_service() end end describe "when reloading a service" do it "should call 'reload' on the service if it supports it" do @new_resource.supports({:reload => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef reload") @provider.reload_service() end it "should should run the user specified reload command if one is specified and the service doesn't support reload" do @new_resource.reload_command("/etc/init.d/chef lollerpants") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef lollerpants") @provider.reload_service() end end describe "when a custom command has been specified" do before do @new_resource.start_command("/etc/init.d/chef startyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef startyousillysally") end it "should still pass all why run assertions" do expect { @provider.run_action(:start) }.not_to raise_error end end end chef-12.3.0/spec/unit/provider/service/systemd_service_spec.rb0000644000004100000410000002674212520074675024527 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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 'spec_helper' describe Chef::Provider::Service::Systemd do let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:service_name) { "rsyslog.service" } let(:new_resource) { Chef::Resource::Service.new(service_name) } let(:provider) { Chef::Provider::Service::Systemd.new(new_resource, run_context) } let(:shell_out_success) do double('shell_out_with_systems_locale', :exitstatus => 0, :error? => false) end let(:shell_out_failure) do double('shell_out_with_systems_locale', :exitstatus => 1, :error? => true) end let(:current_resource) { Chef::Resource::Service.new(service_name) } before(:each) do allow(Chef::Resource::Service).to receive(:new).with(service_name).and_return(current_resource) end describe "load_current_resource" do before(:each) do allow(provider).to receive(:is_active?).and_return(false) allow(provider).to receive(:is_enabled?).and_return(false) end it "should create a current resource with the name of the new resource" do expect(Chef::Resource::Service).to receive(:new).with(new_resource.name).and_return(current_resource) provider.load_current_resource end it "should set the current resources service name to the new resources service name" do provider.load_current_resource expect(current_resource.service_name).to eql(service_name) end it "should check if the service is running" do expect(provider).to receive(:is_active?) provider.load_current_resource end it "should set running to true if the service is running" do allow(provider).to receive(:is_active?).and_return(true) provider.load_current_resource expect(current_resource.running).to be true end it "should set running to false if the service is not running" do allow(provider).to receive(:is_active?).and_return(false) provider.load_current_resource expect(current_resource.running).to be false end describe "when a status command has been specified" do before do allow(new_resource).to receive(:status_command).and_return("/bin/chefhasmonkeypants status") end it "should run the services status command if one has been specified" do allow(provider).to receive(:shell_out).and_return(shell_out_success) provider.load_current_resource expect(current_resource.running).to be true end it "should run the services status command if one has been specified and properly set status check state" do allow(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(shell_out_success) provider.load_current_resource expect(provider.status_check_success).to be true end it "should set running to false if a status command fails" do allow(provider).to receive(:shell_out).and_return(shell_out_failure) provider.load_current_resource expect(current_resource.running).to be false end it "should update state to indicate status check failed when a status command fails" do allow(provider).to receive(:shell_out).and_return(shell_out_failure) provider.load_current_resource expect(provider.status_check_success).to be false end end it "should check if the service is enabled" do expect(provider).to receive(:is_enabled?) provider.load_current_resource end it "should set enabled to true if the service is enabled" do allow(provider).to receive(:is_enabled?).and_return(true) provider.load_current_resource expect(current_resource.enabled).to be true end it "should set enabled to false if the service is not enabled" do allow(provider).to receive(:is_enabled?).and_return(false) provider.load_current_resource expect(current_resource.enabled).to be false end it "should return the current resource" do expect(provider.load_current_resource).to eql(current_resource) end end def setup_current_resource provider.current_resource = current_resource current_resource.service_name(service_name) end %w{/usr/bin/systemctl /bin/systemctl}.each do |systemctl_path| describe "when systemctl path is #{systemctl_path}" do before(:each) do setup_current_resource allow(provider).to receive(:which).with("systemctl").and_return(systemctl_path) end describe "start and stop service" do it "should call the start command if one is specified" do allow(new_resource).to receive(:start_command).and_return("/sbin/rsyslog startyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog startyousillysally") provider.start_service end it "should call '#{systemctl_path} start service_name' if no start command is specified" do expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} start #{service_name}").and_return(shell_out_success) provider.start_service end it "should not call '#{systemctl_path} start service_name' if it is already running" do current_resource.running(true) expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} start #{service_name}") provider.start_service end it "should call the restart command if one is specified" do current_resource.running(true) allow(new_resource).to receive(:restart_command).and_return("/sbin/rsyslog restartyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog restartyousillysally") provider.restart_service end it "should call '#{systemctl_path} restart service_name' if no restart command is specified" do current_resource.running(true) expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} restart #{service_name}").and_return(shell_out_success) provider.restart_service end describe "reload service" do context "when a reload command is specified" do it "should call the reload command" do current_resource.running(true) allow(new_resource).to receive(:reload_command).and_return("/sbin/rsyslog reloadyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog reloadyousillysally") provider.reload_service end end context "when a reload command is not specified" do it "should call '#{systemctl_path} reload service_name' if the service is running" do current_resource.running(true) expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} reload #{service_name}").and_return(shell_out_success) provider.reload_service end it "should start the service if the service is not running" do current_resource.running(false) expect(provider).to receive(:start_service).and_return(true) provider.reload_service end end end it "should call the stop command if one is specified" do current_resource.running(true) allow(new_resource).to receive(:stop_command).and_return("/sbin/rsyslog stopyousillysally") expect(provider).to receive(:shell_out_with_systems_locale!).with("/sbin/rsyslog stopyousillysally") provider.stop_service end it "should call '#{systemctl_path} stop service_name' if no stop command is specified" do current_resource.running(true) expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} stop #{service_name}").and_return(shell_out_success) provider.stop_service end it "should not call '#{systemctl_path} stop service_name' if it is already stopped" do current_resource.running(false) expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} stop #{service_name}") provider.stop_service end end describe "enable and disable service" do before(:each) do provider.current_resource = current_resource current_resource.service_name(service_name) allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}") end it "should call '#{systemctl_path} enable service_name' to enable the service" do expect(provider).to receive(:shell_out!).with("#{systemctl_path} enable #{service_name}").and_return(shell_out_success) provider.enable_service end it "should call '#{systemctl_path} disable service_name' to disable the service" do expect(provider).to receive(:shell_out!).with("#{systemctl_path} disable #{service_name}").and_return(shell_out_success) provider.disable_service end end describe "is_active?" do before(:each) do provider.current_resource = current_resource current_resource.service_name(service_name) allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}") end it "should return true if '#{systemctl_path} is-active service_name' returns 0" do expect(provider).to receive(:shell_out).with("#{systemctl_path} is-active #{service_name} --quiet").and_return(shell_out_success) expect(provider.is_active?).to be true end it "should return false if '#{systemctl_path} is-active service_name' returns anything except 0" do expect(provider).to receive(:shell_out).with("#{systemctl_path} is-active #{service_name} --quiet").and_return(shell_out_failure) expect(provider.is_active?).to be false end end describe "is_enabled?" do before(:each) do provider.current_resource = current_resource current_resource.service_name(service_name) allow(provider).to receive(:which).with("systemctl").and_return("#{systemctl_path}") end it "should return true if '#{systemctl_path} is-enabled service_name' returns 0" do expect(provider).to receive(:shell_out).with("#{systemctl_path} is-enabled #{service_name} --quiet").and_return(shell_out_success) expect(provider.is_enabled?).to be true end it "should return false if '#{systemctl_path} is-enabled service_name' returns anything except 0" do expect(provider).to receive(:shell_out).with("#{systemctl_path} is-enabled #{service_name} --quiet").and_return(shell_out_failure) expect(provider.is_enabled?).to be false end end end end end chef-12.3.0/spec/unit/provider/service/simple_service_spec.rb0000644000004100000410000001601612520074675024321 0ustar www-datawww-data# # Author:: Mathieu Sauve-Frankel # Copyright:: Copyright (c) 2009, Mathieu Sauve Frankel # 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::Service::Simple, "load_current_resource" do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => "ps -ef"} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Simple.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ) aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash aj 8119 6041 0 21:34 pts/3 00:00:03 vi simple_service_spec.rb NOMOCKINGSTRINGSPLZ @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) end it "should create a current resource with the name of the new resource" do expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources service name to the new resources service name" do expect(@current_resource).to receive(:service_name).with(@new_resource.service_name) @provider.load_current_resource end it "should raise error if the node has a nil ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => nil} @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should raise error if the node has an empty ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => ""} @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end describe "when we have a 'ps' attribute" do it "should shell_out! the node's ps command" do expect(@provider).to receive(:shell_out!).with(@node[:command][:ps]).and_return(@status) @provider.load_current_resource end it "should read stdout of the ps command" do allow(@provider).to receive(:shell_out!).and_return(@status) expect(@stdout).to receive(:each_line).and_return(true) @provider.load_current_resource end it "should set running to true if the regex matches the output" do @stdout = StringIO.new(<<-NOMOCKINGSTRINGSPLZ) aj 7842 5057 0 21:26 pts/2 00:00:06 chef aj 7842 5057 0 21:26 pts/2 00:00:06 poos NOMOCKINGSTRINGSPLZ @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the regex doesn't match" do allow(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end it "should raise an exception if ps fails" do allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.action = :start @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end it "should return the current resource" do expect(@provider.load_current_resource).to eql(@current_resource) end describe "when starting the service" do it "should call the start command if one is specified" do allow(@new_resource).to receive(:start_command).and_return("#{@new_resource.start_command}") expect(@provider).to receive(:shell_out_with_systems_locale!).with("#{@new_resource.start_command}") @provider.start_service() end it "should raise an exception if no start command is specified" do @provider.define_resource_requirements @provider.action = :start expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end describe "when stopping a service" do it "should call the stop command if one is specified" do @new_resource.stop_command("/etc/init.d/themadness stop") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/themadness stop") @provider.stop_service() end it "should raise an exception if no stop command is specified" do @provider.define_resource_requirements @provider.action = :stop expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end describe Chef::Provider::Service::Simple, "restart_service" do it "should call the restart command if one has been specified" do @new_resource.restart_command("/etc/init.d/foo restart") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/foo restart") @provider.restart_service() end it "should raise an exception if the resource doesn't support restart, no restart command is provided, and no stop command is provided" do @provider.define_resource_requirements @provider.action = :restart expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do expect(@provider).to receive(:stop_service) expect(@provider).to receive(:sleep).with(1) expect(@provider).to receive(:start_service) @provider.restart_service() end end describe Chef::Provider::Service::Simple, "reload_service" do it "should raise an exception if reload is requested but no command is specified" do @provider.define_resource_requirements @provider.action = :reload expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::UnsupportedAction) end it "should should run the user specified reload command if one is specified" do @new_resource.reload_command("kill -9 1") expect(@provider).to receive(:shell_out_with_systems_locale!).with("kill -9 1") @provider.reload_service() end end end chef-12.3.0/spec/unit/provider/service/aixinit_service_spec.rb0000644000004100000410000002156312520074675024500 0ustar www-datawww-data# # Author:: kaustubh () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Service::AixInit do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => 'fuuuu'} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::AixInit.new(@new_resource, @run_context) @current_resource = Chef::Resource::Service.new("chef") @provider.current_resource = @current_resource @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil end describe "load_current_resource" do it "sets current resource attributes" do expect(@provider).to receive(:set_current_resource_attributes) @provider.load_current_resource end end describe "action_enable" do shared_examples_for "the service is up to date" do it "does not enable the service" do expect(@provider).not_to receive(:enable_service) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).not_to be_updated end end shared_examples_for "the service is not up to date" do it "enables the service and sets the resource as updated" do expect(@provider).to receive(:enable_service).and_return(true) @provider.action_enable @provider.set_updated_status expect(@provider.new_resource).to be_updated end end context "when the service is disabled" do before do @current_resource.enabled(false) end it_behaves_like "the service is not up to date" end context "when the service is enabled" do before do @current_resource.enabled(true) @current_resource.priority(80) end context "and the service sets no priority" do it_behaves_like "the service is up to date" end context "and the service requests the same priority as is set" do before do @new_resource.priority(80) end it_behaves_like "the service is up to date" end context "and the service requests a different priority than is set" do before do @new_resource.priority(20) end it_behaves_like "the service is not up to date" end end end describe "enable_service" do before do allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return([]) end context "when the service doesn't set a priority" do it "creates symlink with status S" do expect(@provider).to receive(:create_symlink).with(2,'S','') @provider.enable_service end end context "when the service sets a simple priority (integer)" do before do @new_resource.priority(75) end it "creates a symlink with status S and a priority" do expect(@provider).to receive(:create_symlink).with(2,'S',75) @provider.enable_service end end context "when the service sets complex priorities (hash)" do before do priority = {2 => [:start, 20], 3 => [:stop, 10]} @new_resource.priority(priority) end it "create symlink with status start (S) or stop (K) and a priority " do expect(@provider).to receive(:create_symlink).with(2,'S',20) expect(@provider).to receive(:create_symlink).with(3,'K',10) @provider.enable_service end end end describe "disable_service" do before do allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return([]) end context "when the service doesn't set a priority" do it "creates symlinks with status stop (K)" do expect(@provider).to receive(:create_symlink).with(2,'K','') @provider.disable_service end end context "when the service sets a simple priority (integer)" do before do @new_resource.priority(75) end it "create symlink with status stop (k) and a priority " do expect(@provider).to receive(:create_symlink).with(2,'K',25) @provider.disable_service end end context "when the service sets complex priorities (hash)" do before do @priority = {2 => [:start, 20], 3 => [:stop, 10]} @new_resource.priority(@priority) end it "create symlink with status stop (k) and a priority " do expect(@provider).to receive(:create_symlink).with(3,'K',90) @provider.disable_service end end end describe "set_current_resource_attributes" do context "when rc2.d contains only start script" do before do files = ["/etc/rc.d/rc2.d/S20apache"] allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]chef"]).and_return(files) end it "the service is enabled" do expect(@provider.current_resource).to receive(:enabled).with(true) expect(@provider.current_resource).to receive(:priority).with(20) @provider.set_current_resource_attributes end end context "when rc2.d contains only stop script" do before do files = ["/etc/rc.d/rc2.d/K20apache"] @priority = {2 => [:stop, 20]} allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]chef"]).and_return(files) end it "the service is not enabled" do expect(@provider.current_resource).to receive(:enabled).with(false) expect(@provider.current_resource).to receive(:priority).with(@priority) @provider.set_current_resource_attributes end end context "when rc2.d contains both start and stop scripts" do before do @files = ["/etc/rc.d/rc2.d/S20apache", "/etc/rc.d/rc2.d/K80apache"] @priority = {2 => [:start, 20], 2 => [:stop, 80]} allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]chef"]).and_return(@files) end it "the service is enabled" do expect(@current_resource).to receive(:enabled).with(true) expect(@current_resource).to receive(:priority).with(@priority) @provider.set_current_resource_attributes end end context "when rc2.d contains only start script (without priority)" do before do files = ["/etc/rc.d/rc2.d/Sapache"] allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return(files) end it "the service is enabled" do expect(@provider.current_resource).to receive(:enabled).with(true) expect(@provider.current_resource).to receive(:priority).with('') @provider.set_current_resource_attributes end end context "when rc2.d contains only stop script (without priority)" do before do files = ["/etc/rc.d/rc2.d/Kapache"] @priority = {2 => [:stop, '']} allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return(files) end it "the service is not enabled" do expect(@provider.current_resource).to receive(:enabled).with(false) expect(@provider.current_resource).to receive(:priority).with(@priority) @provider.set_current_resource_attributes end end context "when rc2.d contains both start and stop scripts" do before do files = ["/etc/rc.d/rc2.d/Sapache", "/etc/rc.d/rc2.d/Kapache"] @priority = {2 => [:start, ''], 2 => [:stop, '']} allow(Dir).to receive(:glob).with(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).and_return(files) end it "the service is enabled" do expect(@current_resource).to receive(:enabled).with(true) expect(@current_resource).to receive(:priority).with(@priority) @provider.set_current_resource_attributes end end end end chef-12.3.0/spec/unit/provider/service/invokercd_service_spec.rb0000644000004100000410000002171512520074675025016 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' describe Chef::Provider::Service::Invokercd, "load_current_resource" do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => "ps -ef"} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Invokercd.new(@new_resource, @run_context) allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) @stdout = StringIO.new(<<-PS) aj 7842 5057 0 21:26 pts/2 00:00:06 vi init.rb aj 7903 5016 0 21:26 pts/5 00:00:00 /bin/bash aj 8119 6041 0 21:34 pts/3 00:00:03 vi init_service_spec.rb PS @status = double("Status", :exitstatus => 0, :stdout => @stdout) allow(@provider).to receive(:shell_out!).and_return(@status) end it "should create a current resource with the name of the new resource" do @provider.load_current_resource expect(@provider.current_resource).to equal(@current_resource) end it "should set the current resources service name to the new resources service name" do @provider.load_current_resource expect(@current_resource.service_name).to eq('chef') end describe "when the service supports status" do before do @new_resource.supports({:status => true}) end it "should run '/usr/sbin/invoke-rc.d service_name status'" do expect(@provider).to receive(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource end it "should set running to true if the status command returns 0" do allow(@provider).to receive(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the status command returns anything except 0" do allow(@status).to receive(:exitstatus).and_return(1) allow(@provider).to receive(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end it "should set running to false if the status command raises" do allow(@provider).to receive(:shell_out).with("/usr/sbin/invoke-rc.d #{@current_resource.service_name} status").and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.load_current_resource expect(@current_resource.running).to be_falsey end end describe "when a status command has been specified" do before do allow(@new_resource).to receive(:status_command).and_return("/usr/sbin/invoke-rc.d chefhasmonkeypants status") end it "should run the services status command if one has been specified" do expect(@provider).to receive(:shell_out).with("/usr/sbin/invoke-rc.d chefhasmonkeypants status").and_return(@status) @provider.load_current_resource end end describe "when the node has not specified a ps command" do it "should raise error if the node has a nil ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => nil} @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should raise error if the node has an empty ps attribute and no other means to get status" do @node.automatic_attrs[:command] = {:ps => ""} @provider.action = :start @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end describe "when we have a 'ps' attribute" do it "should shell_out! the node's ps command" do @status = double("Status", :exitstatus => 0, :stdout => @stdout) expect(@provider).to receive(:shell_out!).with(@node[:command][:ps]).and_return(@status) @provider.load_current_resource end it "should set running to true if the regex matches the output" do @stdout = StringIO.new(<<-RUNNING_PS) aj 7842 5057 0 21:26 pts/2 00:00:06 chef aj 7842 5057 0 21:26 pts/2 00:00:06 poos RUNNING_PS @status = double("Status", :exitstatus => 0, :stdout => @stdout) expect(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_truthy end it "should set running to false if the regex doesn't match" do @status = double("Status", :exitstatus => 0, :stdout => @stdout) expect(@provider).to receive(:shell_out!).and_return(@status) @provider.load_current_resource expect(@current_resource.running).to be_falsey end it "should raise an exception if ps fails" do allow(@provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) @provider.action = :start @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end it "should return the current resource" do expect(@provider.load_current_resource).to eql(@current_resource) end describe "when starting the service" do it "should call the start command if one is specified" do @new_resource.start_command("/usr/sbin/invoke-rc.d chef startyousillysally") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef startyousillysally") @provider.start_service() end it "should call '/usr/sbin/invoke-rc.d service_name start' if no start command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} start") @provider.start_service() end end describe Chef::Provider::Service::Invokercd, "stop_service" do it "should call the stop command if one is specified" do @new_resource.stop_command("/usr/sbin/invoke-rc.d chef itoldyoutostop") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef itoldyoutostop") @provider.stop_service() end it "should call '/usr/sbin/invoke-rc.d service_name stop' if no stop command is specified" do expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} stop") @provider.stop_service() end end describe "when restarting a service" do it "should call 'restart' on the service_name if the resource supports it" do @new_resource.supports({:restart => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restart") @provider.restart_service() end it "should call the restart_command if one has been specified" do @new_resource.restart_command("/usr/sbin/invoke-rc.d chef restartinafire") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d #{@new_resource.service_name} restartinafire") @provider.restart_service() end it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do expect(@provider).to receive(:stop_service) expect(@provider).to receive(:sleep).with(1) expect(@provider).to receive(:start_service) @provider.restart_service() end end describe "when reloading a service" do it "should call 'reload' on the service if it supports it" do @new_resource.supports({:reload => true}) expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef reload") @provider.reload_service() end it "should should run the user specified reload command if one is specified and the service doesn't support reload" do @new_resource.reload_command("/usr/sbin/invoke-rc.d chef lollerpants") expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/invoke-rc.d chef lollerpants") @provider.reload_service() end end end chef-12.3.0/spec/unit/provider/service/macosx_spec.rb0000644000004100000410000003151512520074675022603 0ustar www-datawww-data# # Author:: Igor Afonov # Copyright:: Copyright (c) 2011 Igor Afonov # 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::Service::Macosx do describe ".gather_plist_dirs" do context "when HOME directory is set" do before do allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_yield('/Users/someuser/Library/LaunchAgents') end it "includes users's LaunchAgents folder" do expect(described_class.gather_plist_dirs).to include("/Users/someuser/Library/LaunchAgents") end end context "when HOME directory is not set" do before do allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_return(nil) end it "doesn't include user's LaunchAgents folder" do expect(described_class.gather_plist_dirs).not_to include("~/Library/LaunchAgents") end end end context "when service name is given as" do let(:node) { Chef::Node.new } let(:events) {Chef::EventDispatch::Dispatcher.new} let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:provider) { described_class.new(new_resource, run_context) } let(:launchctl_stdout) { StringIO.new } let(:plutil_stdout) { String.new <<-XML } Label io.redis.redis-server XML ["Daemon", "Agent"].each do |service_type| ["redis-server", "io.redis.redis-server"].each do |service_name| ["10.9", "10.10"].each do |platform_version| let(:plist) {'/Library/LaunchDaemons/io.redis.redis-server.plist'} let(:session) { StringIO.new } if service_type == 'Agent' let(:plist) {'/Library/LaunchAgents/io.redis.redis-server.plist'} let(:session) {'-S Aqua '} let(:su_cmd) {'su igor -c'} if platform_version != "10.10" let(:su_cmd) {'su -l igor -c'} end end let(:service_label) {'io.redis.redis-server'} before do allow(Dir).to receive(:glob).and_return([plist], []) allow(Etc).to receive(:getlogin).and_return('igor') allow(node).to receive(:[]).with("platform_version").and_return(platform_version) cmd = "launchctl list #{service_label}" allow(provider).to receive(:shell_out_with_systems_locale). with(/(#{su_cmd} '#{cmd}'|#{cmd})/). and_return(double("Status", :stdout => launchctl_stdout, :exitstatus => 0)) allow(File).to receive(:exists?).and_return([true], []) allow(provider).to receive(:shell_out_with_systems_locale!). with(/plutil -convert xml1 -o/). and_return(double("Status", :stdout => plutil_stdout)) end context "#{service_name} that is a #{service_type} running Osx #{platform_version}" do let(:new_resource) { Chef::Resource::MacosxService.new(service_name) } let!(:current_resource) { Chef::Resource::MacosxService.new(service_name) } describe "#load_current_resource" do # CHEF-5223 "you can't glob for a file that hasn't been converged # onto the node yet." context "when the plist doesn't exist" do def run_resource_setup_for_action(action) new_resource.action(action) provider.action = action provider.load_current_resource provider.define_resource_requirements provider.process_resource_requirements end before do allow(Dir).to receive(:glob).and_return([]) allow(File).to receive(:exists?).and_return([true], []) allow(provider).to receive(:shell_out!). with(/plutil -convert xml1 -o/). and_raise(Mixlib::ShellOut::ShellCommandFailed) end it "works for action :nothing" do expect { run_resource_setup_for_action(:nothing) }.not_to raise_error end it "works for action :start" do expect { run_resource_setup_for_action(:start) }.not_to raise_error end it "errors if action is :enable" do expect { run_resource_setup_for_action(:enable) }.to raise_error(Chef::Exceptions::Service) end it "errors if action is :disable" do expect { run_resource_setup_for_action(:disable) }.to raise_error(Chef::Exceptions::Service) end end context "when launchctl returns pid in service list" do let(:launchctl_stdout) { StringIO.new <<-SVC_LIST } { "LimitLoadToSessionType" = "System"; "Label" = "io.redis.redis-server"; "TimeOut" = 30; "OnDemand" = false; "LastExitStatus" = 0; "PID" = 62803; "Program" = "do_some.sh"; "ProgramArguments" = ( "path/to/do_something.sh"; "-f"; ); }; SVC_LIST before do provider.load_current_resource end it "sets resource running state to true" do expect(provider.current_resource.running).to be_truthy end it "sets resouce enabled state to true" do expect(provider.current_resource.enabled).to be_truthy end end describe "running unsupported actions" do before do allow(Dir).to receive(:glob).and_return(["#{plist}"], []) allow(File).to receive(:exists?).and_return([true], []) end it "should throw an exception when reload action is attempted" do expect {provider.run_action(:reload)}.to raise_error(Chef::Exceptions::UnsupportedAction) end end context "when launchctl returns empty service pid" do let(:launchctl_stdout) { StringIO.new <<-SVC_LIST } { "LimitLoadToSessionType" = "System"; "Label" = "io.redis.redis-server"; "TimeOut" = 30; "OnDemand" = false; "LastExitStatus" = 0; "Program" = "do_some.sh"; "ProgramArguments" = ( "path/to/do_something.sh"; "-f"; ); }; SVC_LIST before do provider.load_current_resource end it "sets resource running state to false" do expect(provider.current_resource.running).to be_falsey end it "sets resouce enabled state to true" do expect(provider.current_resource.enabled).to be_truthy end end context "when launchctl doesn't return service entry at all" do let(:launchctl_stdout) { StringIO.new <<-SVC_LIST } Could not find service "io.redis.redis-server" in domain for system SVC_LIST it "sets service running state to false" do provider.load_current_resource expect(provider.current_resource.running).to be_falsey end context "and plist for service is not available" do before do allow(Dir).to receive(:glob).and_return([]) provider.load_current_resource end it "sets resouce enabled state to false" do expect(provider.current_resource.enabled).to be_falsey end end context "and plist for service is available" do before do allow(Dir).to receive(:glob).and_return(["#{plist}"], []) provider.load_current_resource end it "sets resouce enabled state to true" do expect(provider.current_resource.enabled).to be_truthy end end describe "and several plists match service name" do it "throws exception" do allow(Dir).to receive(:glob).and_return(["#{plist}", "/Users/wtf/something.plist"]) provider.load_current_resource provider.define_resource_requirements expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end end end describe "#start_service" do before do allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource) provider.load_current_resource allow(current_resource).to receive(:running).and_return(false) end it "calls the start command if one is specified and service is not running" do allow(new_resource).to receive(:start_command).and_return("cowsay dirty") expect(provider).to receive(:shell_out_with_systems_locale!).with("cowsay dirty") provider.start_service end it "shows warning message if service is already running" do allow(current_resource).to receive(:running).and_return(true) expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] already running, not starting") provider.start_service end it "starts service via launchctl if service found" do cmd = 'launchctl load -w ' + session + plist expect(provider).to receive(:shell_out_with_systems_locale). with(/(#{su_cmd} .#{cmd}.|#{cmd})/). and_return(0) provider.start_service end end describe "#stop_service" do before do allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource) provider.load_current_resource allow(current_resource).to receive(:running).and_return(true) end it "calls the stop command if one is specified and service is running" do allow(new_resource).to receive(:stop_command).and_return("kill -9 123") expect(provider).to receive(:shell_out_with_systems_locale!).with("kill -9 123") provider.stop_service end it "shows warning message if service is not running" do allow(current_resource).to receive(:running).and_return(false) expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] not running, not stopping") provider.stop_service end it "stops the service via launchctl if service found" do cmd = 'launchctl unload -w '+ plist expect(provider).to receive(:shell_out_with_systems_locale). with(/(#{su_cmd} .#{cmd}.|#{cmd})/). and_return(0) provider.stop_service end end describe "#restart_service" do before do allow(Chef::Resource::Service).to receive(:new).and_return(current_resource) provider.load_current_resource allow(current_resource).to receive(:running).and_return(true) allow(provider).to receive(:sleep) end it "issues a command if given" do allow(new_resource).to receive(:restart_command).and_return("reload that thing") expect(provider).to receive(:shell_out_with_systems_locale!).with("reload that thing") provider.restart_service end it "stops and then starts service" do expect(provider).to receive(:unload_service) expect(provider).to receive(:load_service); provider.restart_service end end end end end end end end chef-12.3.0/spec/unit/provider/service/redhat_spec.rb0000644000004100000410000001631712520074675022563 0ustar www-datawww-data# # Author:: AJ Christensen () # Copyright:: Copyright (c) 2008 HJK Solutions, LLC # 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper")) require 'ostruct' shared_examples_for "define_resource_requirements_common" do it "should raise an error if /sbin/chkconfig does not exist" do allow(File).to receive(:exists?).with("/sbin/chkconfig").and_return(false) allow(@provider).to receive(:shell_out).with("/sbin/service chef status").and_raise(Errno::ENOENT) allow(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_raise(Errno::ENOENT) @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end it "should not raise an error if the service exists but is not added to any runlevels" do status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "") expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status) chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "", :stderr => "service chef supports chkconfig, but is not referenced in any runlevel (run 'chkconfig --add chef')") expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.not_to raise_error end end describe "Chef::Provider::Service::Redhat" do before(:each) do @node = Chef::Node.new @node.automatic_attrs[:command] = {:ps => 'foo'} @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Service.new("chef") @current_resource = Chef::Resource::Service.new("chef") @provider = Chef::Provider::Service::Redhat.new(@new_resource, @run_context) @provider.action = :start allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource) allow(File).to receive(:exists?).with("/sbin/chkconfig").and_return(true) end describe "while not in why run mode" do before(:each) do Chef::Config[:why_run] = false end describe "load current resource" do it "sets the current enabled status to true if the service is enabled for any run level" do status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "") expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status) chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "") expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) expect(@provider.instance_variable_get("@service_missing")).to be_falsey @provider.load_current_resource expect(@current_resource.enabled).to be_truthy end it "sets the current enabled status to false if the regex does not match" do status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "") expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status) chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:off 6:off", :stderr => "") expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) expect(@provider.instance_variable_get("@service_missing")).to be_falsey expect(@provider.load_current_resource).to eql(@current_resource) expect(@current_resource.enabled).to be_falsey end end describe "define resource requirements" do it_should_behave_like "define_resource_requirements_common" context "when the service does not exist" do before do status = double("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service") expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status) chkconfig = double("Chkconfig", :existatus=> 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory") expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) @provider.load_current_resource @provider.define_resource_requirements end [ "start", "reload", "restart", "enable" ].each do |action| it "should raise an error when the action is #{action}" do @provider.action = action expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service) end end [ "stop", "disable" ].each do |action| it "should not raise an error when the action is #{action}" do @provider.action = action expect { @provider.process_resource_requirements }.not_to raise_error end end end end end describe "while in why run mode" do before(:each) do Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end describe "define resource requirements" do it_should_behave_like "define_resource_requirements_common" it "should not raise an error if the service does not exist" do status = double("Status", :exitstatus => 1, :stdout => "", :stderr => "chef: unrecognized service") expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status) chkconfig = double("Chkconfig", :existatus=> 1, :stdout => "", :stderr => "error reading information on service chef: No such file or directory") expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig) @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.not_to raise_error end end end describe "enable_service" do it "should call chkconfig to add 'service_name'" do expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} on") @provider.enable_service end end describe "disable_service" do it "should call chkconfig to del 'service_name'" do expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} off") @provider.disable_service end end end chef-12.3.0/spec/unit/provider/deploy_spec.rb0000644000004100000410000007321612520074675021151 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' describe Chef::Provider::Deploy do before do allow(Chef::Platform).to receive(:windows?) { false } @release_time = Time.utc( 2004, 8, 15, 16, 23, 42) allow(Time).to receive(:now).and_return(@release_time) @expected_release_dir = "/my/deploy/dir/releases/20040815162342" @resource = Chef::Resource::Deploy.new("/my/deploy/dir") @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @provider = Chef::Provider::Deploy.new(@resource, @run_context) allow(@provider).to receive(:release_slug) allow(@provider).to receive(:release_path).and_return(@expected_release_dir) end it "loads scm resource" do expect(@provider.scm_provider).to receive(:load_current_resource) @provider.load_current_resource end it "supports :deploy and :rollback actions" do expect(@provider).to respond_to(:action_deploy) expect(@provider).to respond_to(:action_rollback) end context "when the deploy resource has a timeout attribute" do let(:ten_seconds) { 10 } before { @resource.timeout(ten_seconds) } it "relays the timeout to the scm resource" do expect(@provider.scm_provider.new_resource.timeout).to eq(ten_seconds) end end context "when the deploy resource has no timeout attribute" do it "should not set a timeout on the scm resource" do expect(@provider.scm_provider.new_resource.timeout).to be_nil end end context "when the deploy_to dir does not exist yet" do before do expect(FileUtils).to receive(:mkdir_p).with(@resource.deploy_to).ordered expect(FileUtils).to receive(:mkdir_p).with(@resource.shared_path).ordered allow(::File).to receive(:directory?).and_return(false) allow(@provider).to receive(:symlink) allow(@provider).to receive(:migrate) allow(@provider).to receive(:copy_cached_repo) end it "creates deploy_to dir" do expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times expect(@provider).to receive(:enforce_ownership).twice allow(@provider).to receive(:update_cached_repo) @provider.deploy end end it "does not create deploy_to dir if it exists" do allow(::File).to receive(:directory?).and_return(true) expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times expect(FileUtils).not_to receive(:mkdir_p).with(@resource.deploy_to) expect(FileUtils).not_to receive(:mkdir_p).with(@resource.shared_path) expect(@provider).to receive(:enforce_ownership).twice allow(@provider).to receive(:copy_cached_repo) allow(@provider).to receive(:update_cached_repo) allow(@provider).to receive(:symlink) allow(@provider).to receive(:migrate) @provider.deploy end it "ensures the deploy_to dir ownership after the verfication that it exists" do expect(::Dir).to receive(:chdir).with(@expected_release_dir).exactly(4).times expect(@provider).to receive(:verify_directories_exist).ordered expect(@provider).to receive(:enforce_ownership).ordered allow(@provider).to receive(:copy_cached_repo) allow(@provider).to receive(:update_cached_repo) allow(@provider).to receive(:install_gems) expect(@provider).to receive(:enforce_ownership).ordered allow(@provider).to receive(:enforce_ownership) allow(@provider).to receive(:symlink) allow(@provider).to receive(:migrate) @provider.deploy end it "updates and copies the repo, then does a migrate, symlink, restart, restart, cleanup on deploy" do allow(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir") allow(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir/shared") expect(@provider).to receive(:enforce_ownership).twice expect(@provider).to receive(:update_cached_repo) expect(@provider).to receive(:copy_cached_repo) expect(@provider).to receive(:install_gems) expect(@provider).to receive(:callback).with(:before_migrate, nil) expect(@provider).to receive(:migrate) expect(@provider).to receive(:callback).with(:before_symlink, nil) expect(@provider).to receive(:symlink) expect(@provider).to receive(:callback).with(:before_restart, nil) expect(@provider).to receive(:restart) expect(@provider).to receive(:callback).with(:after_restart, nil) expect(@provider).to receive(:cleanup!) @provider.deploy end it "should not deploy if there is already a deploy at release_path, and it is the current release" do allow(@provider).to receive(:all_releases).and_return([@expected_release_dir]) allow(@provider).to receive(:current_release?).with(@expected_release_dir).and_return(true) expect(@provider).not_to receive(:deploy) @provider.run_action(:deploy) end it "should call action_rollback if there is already a deploy of this revision at release_path, and it is not the current release" do allow(@provider).to receive(:all_releases).and_return([@expected_release_dir, "102021"]) allow(@provider).to receive(:current_release?).with(@expected_release_dir).and_return(false) expect(@provider).to receive(:rollback_to).with(@expected_release_dir) expect(@provider).to receive(:current_release?) @provider.run_action(:deploy) end it "calls deploy when deploying a new release" do allow(@provider).to receive(:all_releases).and_return([]) expect(@provider).to receive(:deploy) @provider.run_action(:deploy) end it "runs action svn_force_export when new_resource.svn_force_export is true" do @resource.svn_force_export true expect(@provider.scm_provider).to receive(:run_action).with(:force_export) @provider.update_cached_repo end it "Removes the old release before deploying when force deploying over it" do allow(@provider).to receive(:all_releases).and_return([@expected_release_dir]) expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir) expect(@provider).to receive(:deploy) @provider.run_action(:force_deploy) end it "deploys as normal when force deploying and there's no prior release at the same path" do allow(@provider).to receive(:all_releases).and_return([]) expect(@provider).to receive(:deploy) @provider.run_action(:force_deploy) end it "dont care by default if error happens on deploy" do allow(@provider).to receive(:all_releases).and_return(['previous_release']) allow(@provider).to receive(:deploy){ raise "Unexpected error" } allow(@provider).to receive(:previous_release_path).and_return('previous_release') expect(@provider).not_to receive(:rollback) expect { @provider.run_action(:deploy) }.to raise_exception(RuntimeError, "Unexpected error") end it "rollbacks to previous release if error happens on deploy" do @resource.rollback_on_error true allow(@provider).to receive(:all_releases).and_return(['previous_release']) allow(@provider).to receive(:deploy){ raise "Unexpected error" } allow(@provider).to receive(:previous_release_path).and_return('previous_release') expect(@provider).to receive(:rollback) expect { @provider.run_action(:deploy) }.to raise_exception(RuntimeError, "Unexpected error") end describe "on systems without broken Dir.glob results" do it "sets the release path to the penultimate release when one is not specified, symlinks, and rm's the last release on rollback" do allow(@provider).to receive(:release_path).and_return("/my/deploy/dir/releases/3") all_releases = ["/my/deploy/dir/releases/1", "/my/deploy/dir/releases/2", "/my/deploy/dir/releases/3", "/my/deploy/dir/releases/4", "/my/deploy/dir/releases/5"] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect(@provider).to receive(:symlink) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/4") expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/5") @provider.run_action(:rollback) expect(@provider.release_path).to eql("/my/deploy/dir/releases/3") expect(@provider.shared_path).to eql("/my/deploy/dir/shared") end it "sets the release path to the specified release, symlinks, and rm's any newer releases on rollback" do allow(@provider).to receive(:release_path).and_call_original all_releases = ["/my/deploy/dir/releases/20040815162342", "/my/deploy/dir/releases/20040700000000", "/my/deploy/dir/releases/20040600000000", "/my/deploy/dir/releases/20040500000000"].sort! allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect(@provider).to receive(:symlink) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342") @provider.run_action(:rollback) expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000") expect(@provider.shared_path).to eql("/my/deploy/dir/shared") end it "sets the release path to the penultimate release, symlinks, and rm's the last release on rollback" do allow(@provider).to receive(:release_path).and_call_original all_releases = [ "/my/deploy/dir/releases/20040815162342", "/my/deploy/dir/releases/20040700000000", "/my/deploy/dir/releases/20040600000000", "/my/deploy/dir/releases/20040500000000"] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect(@provider).to receive(:symlink) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342") @provider.run_action(:rollback) expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000") expect(@provider.shared_path).to eql("/my/deploy/dir/shared") end describe "if there are no releases to fallback to" do it "an exception is raised when there is only 1 release" do #@provider.unstub(:release_path) -- unstub the release path on top to feed our own release path all_releases = [ "/my/deploy/dir/releases/20040815162342"] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) #@provider.should_receive(:symlink) #FileUtils.should_receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342") #@provider.run_action(:rollback) #@provider.release_path.should eql(NIL) -- no check needed since assertions will fail expect { @provider.run_action(:rollback) }.to raise_exception(RuntimeError, "There is no release to rollback to!") end it "an exception is raised when there are no releases" do all_releases = [] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect { @provider.run_action(:rollback) }.to raise_exception(RuntimeError, "There is no release to rollback to!") end end end describe "CHEF-628: on systems with broken Dir.glob results" do it "sets the release path to the penultimate release, symlinks, and rm's the last release on rollback" do allow(@provider).to receive(:release_path).and_call_original all_releases = [ "/my/deploy/dir/releases/20040500000000", "/my/deploy/dir/releases/20040600000000", "/my/deploy/dir/releases/20040700000000", "/my/deploy/dir/releases/20040815162342" ] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect(@provider).to receive(:symlink) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342") @provider.run_action(:rollback) expect(@provider.release_path).to eql("/my/deploy/dir/releases/20040700000000") expect(@provider.shared_path).to eql("/my/deploy/dir/shared") end end it "raises a runtime error when there's no release to rollback to" do all_releases = [] allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect {@provider.run_action(:rollback)}.to raise_error(RuntimeError) end it "runs the new resource collection in the runner during a callback" do @runner = double("Runner") allow(Chef::Runner).to receive(:new).and_return(@runner) expect(@runner).to receive(:converge) callback_code = Proc.new { :noop } @provider.callback(:whatevs, callback_code) end it "loads callback files from the release/ dir if the file exists" do foo_callback = @expected_release_dir + "/deploy/foo.rb" expect(::File).to receive(:exist?).with(foo_callback).once.and_return(true) expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield expect(@provider).to receive(:from_file).with(foo_callback) @provider.callback(:foo, "deploy/foo.rb") end it "raises a runtime error if a callback file is explicitly specified but does not exist" do baz_callback = "/deploy/baz.rb" expect(::File).to receive(:exist?).with("#{@expected_release_dir}/#{baz_callback}").and_return(false) @resource.before_migrate baz_callback @provider.define_resource_requirements @provider.action = :deploy expect {@provider.process_resource_requirements}.to raise_error(RuntimeError) end it "runs a default callback if the callback code is nil" do bar_callback = @expected_release_dir + "/deploy/bar.rb" expect(::File).to receive(:exist?).with(bar_callback).and_return(true) expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield expect(@provider).to receive(:from_file).with(bar_callback) @provider.callback(:bar, nil) end it "skips an eval callback if the file doesn't exist" do barbaz_callback = @expected_release_dir + "/deploy/barbaz.rb" expect(::File).to receive(:exist?).with(barbaz_callback).and_return(false) expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield expect(@provider).not_to receive(:from_file) @provider.callback(:barbaz, nil) end # CHEF-3449 #converge_by is called in #recipe_eval and must happen in sequence # with the other calls to #converge_by to keep the train on the tracks it "evaluates a callback file before the corresponding step" do expect(@provider).to receive(:verify_directories_exist) expect(@provider).to receive(:update_cached_repo) expect(@provider).to receive(:enforce_ownership) expect(@provider).to receive(:copy_cached_repo) expect(@provider).to receive(:install_gems) expect(@provider).to receive(:enforce_ownership) expect(@provider).to receive(:converge_by).ordered # before_migrate expect(@provider).to receive(:migrate).ordered expect(@provider).to receive(:converge_by).ordered # before_symlink expect(@provider).to receive(:symlink).ordered expect(@provider).to receive(:converge_by).ordered # before_restart expect(@provider).to receive(:restart).ordered expect(@provider).to receive(:converge_by).ordered # after_restart expect(@provider).to receive(:cleanup!) @provider.deploy end it "gets a SCM provider as specified by its resource" do expect(@provider.scm_provider).to be_an_instance_of(Chef::Provider::Git) expect(@provider.scm_provider.new_resource.destination).to eql("/my/deploy/dir/shared/cached-copy") end it "syncs the cached copy of the repo" do expect(@provider.scm_provider).to receive(:run_action).with(:sync) @provider.update_cached_repo end it "makes a copy of the cached repo in releases dir" do expect(FileUtils).to receive(:mkdir_p).with("/my/deploy/dir/releases") expect(FileUtils).to receive(:cp_r).with("/my/deploy/dir/shared/cached-copy/.", @expected_release_dir, :preserve => true) @provider.copy_cached_repo end it "calls the internal callback :release_created when cleaning up the releases" do allow(FileUtils).to receive(:mkdir_p) allow(FileUtils).to receive(:cp_r) expect(@provider).to receive(:release_created) @provider.cleanup! end it "chowns the whole release dir to user and group specified in the resource" do @resource.user "foo" @resource.group "bar" expect(FileUtils).to receive(:chown_R).with("foo", "bar", "/my/deploy/dir") @provider.enforce_ownership end it "skips the migration when resource.migrate => false but runs symlinks before migration" do @resource.migrate false expect(@provider).not_to receive :run_command expect(@provider).to receive :run_symlinks_before_migrate @provider.migrate end it "links the database.yml and runs resource.migration_command when resource.migrate #=> true" do @resource.migrate true @resource.migration_command "migration_foo" @resource.user "deployNinja" @resource.group "deployNinjas" @resource.environment "RAILS_ENV" => "production" expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/config/database.yml", @expected_release_dir + "/config/database.yml") expect(@provider).to receive(:enforce_ownership) allow(STDOUT).to receive(:tty?).and_return(true) allow(Chef::Log).to receive(:info?).and_return(true) expect(@provider).to receive(:run_command).with(:command => "migration_foo", :cwd => @expected_release_dir, :user => "deployNinja", :group => "deployNinjas", :log_level => :info, :live_stream => STDOUT, :log_tag => "deploy[/my/deploy/dir]", :environment => {"RAILS_ENV"=>"production"}) @provider.migrate end it "purges the current release's /log /tmp/pids/ and /public/system directories" do expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/log") expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/tmp/pids") expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/public/system") @provider.purge_tempfiles_from_current_release end it "symlinks temporary files and logs from the shared dir into the current release" do allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/system") allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/pids") allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/log") expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/tmp") expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/public") expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/config") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/system", @expected_release_dir + "/public/system") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/pids", @expected_release_dir + "/tmp/pids") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/log", @expected_release_dir + "/log") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/config/database.yml", @expected_release_dir + "/config/database.yml") expect(@provider).to receive(:enforce_ownership) @provider.link_tempfiles_to_current_release end it "symlinks the current release dir into production" do expect(FileUtils).to receive(:rm_f).with("/my/deploy/dir/current") expect(FileUtils).to receive(:ln_sf).with(@expected_release_dir, "/my/deploy/dir/current") expect(@provider).to receive(:enforce_ownership) @provider.link_current_release_to_production end context "with a customized app layout" do before do @resource.purge_before_symlink(%w{foo bar}) @resource.create_dirs_before_symlink(%w{baz qux}) @resource.symlinks "foo/bar" => "foo/bar", "baz" => "qux/baz" @resource.symlink_before_migrate "radiohead/in_rainbows.yml" => "awesome" end it "purges the purge_before_symlink directories" do expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/foo") expect(FileUtils).to receive(:rm_rf).with(@expected_release_dir + "/bar") @provider.purge_tempfiles_from_current_release end it "symlinks files from the shared directory to the current release directory" do expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/baz") expect(FileUtils).to receive(:mkdir_p).with(@expected_release_dir + "/qux") allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/foo/bar") allow(FileUtils).to receive(:mkdir_p).with(@resource.shared_path + "/baz") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/foo/bar", @expected_release_dir + "/foo/bar") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/baz", @expected_release_dir + "/qux/baz") expect(FileUtils).to receive(:ln_sf).with("/my/deploy/dir/shared/radiohead/in_rainbows.yml", @expected_release_dir + "/awesome") expect(@provider).to receive(:enforce_ownership) @provider.link_tempfiles_to_current_release end end it "does nothing for restart if restart_command is empty" do expect(@provider).not_to receive(:run_command) @provider.restart end it "runs the restart command in the current application dir when the resource has a restart_command" do @resource.restart_command "restartcmd" expect(@provider).to receive(:run_command).with(:command => "restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug) @provider.restart end it "lists all available releases" do all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000", "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000"].sort! expect(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases) expect(@provider.all_releases).to eql(all_releases) end it "removes all but the 5 newest releases" do all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000", "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000", "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000", "/my/deploy/dir/20040200000000", "/my/deploy/dir/20040100000000"].sort! allow(@provider).to receive(:all_releases).and_return(all_releases) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040100000000") expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040200000000") expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040300000000") @provider.cleanup! end it "removes all but a certain number of releases when the resource has a keep_releases" do @resource.keep_releases 7 all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000", "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000", "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000", "/my/deploy/dir/20040200000000", "/my/deploy/dir/20040100000000"].sort! allow(@provider).to receive(:all_releases).and_return(all_releases) expect(FileUtils).to receive(:rm_rf).with("/my/deploy/dir/20040100000000") @provider.cleanup! end it "fires a callback for :release_deleted when deleting an old release" do all_releases = ["/my/deploy/dir/20040815162342", "/my/deploy/dir/20040700000000", "/my/deploy/dir/20040600000000", "/my/deploy/dir/20040500000000", "/my/deploy/dir/20040400000000", "/my/deploy/dir/20040300000000"].sort! allow(@provider).to receive(:all_releases).and_return(all_releases) allow(FileUtils).to receive(:rm_rf) expect(@provider).to receive(:release_deleted).with("/my/deploy/dir/20040300000000") @provider.cleanup! end it "puts resource.to_hash in @configuration for backwards compat with capistano-esque deploy hooks" do expect(@provider.instance_variable_get(:@configuration)).to eq(@resource.to_hash) end it "sets @configuration[:environment] to the value of RAILS_ENV for backwards compat reasons" do resource = Chef::Resource::Deploy.new("/my/deploy/dir") resource.environment "production" provider = Chef::Provider::Deploy.new(resource, @run_context) expect(provider.instance_variable_get(:@configuration)[:environment]).to eql("production") end it "shouldn't give a no method error on migrate if the environment is nil" do allow(@provider).to receive(:enforce_ownership) allow(@provider).to receive(:run_symlinks_before_migrate) allow(@provider).to receive(:run_command) @provider.migrate end context "using inline recipes for callbacks" do it "runs an inline recipe with the provided block for :callback_name == {:recipe => &block} " do snitch = nil recipe_code = Proc.new {snitch = 42} #@provider.should_receive(:instance_eval).with(&recipe_code) @provider.callback(:whateverz, recipe_code) expect(snitch).to eq(42) end it "loads a recipe file from the specified path and from_file evals it" do expect(::File).to receive(:exist?).with(@expected_release_dir + "/chefz/foobar_callback.rb").once.and_return(true) expect(::Dir).to receive(:chdir).with(@expected_release_dir).and_yield expect(@provider).to receive(:from_file).with(@expected_release_dir + "/chefz/foobar_callback.rb") @provider.callback(:whateverz, "chefz/foobar_callback.rb") end it "instance_evals a block/proc for restart command" do snitch = nil restart_cmd = Proc.new {snitch = 42} @resource.restart(&restart_cmd) @provider.restart expect(snitch).to eq(42) end end describe "API bridge to capistrano" do it "defines sudo as a forwarder to execute" do expect(@provider).to receive(:execute).with("the moon, fool") @provider.sudo("the moon, fool") end it "defines run as a forwarder to execute, setting the user, group, cwd and environment to new_resource.user" do mock_execution = double("Resource::Execute") expect(@provider).to receive(:execute).with("iGoToHell4this").and_return(mock_execution) @resource.user("notCoolMan") @resource.group("Ggroup") @resource.environment("APP_ENV" => 'staging') @resource.deploy_to("/my/app") expect(mock_execution).to receive(:user).with("notCoolMan") expect(mock_execution).to receive(:group).with("Ggroup") expect(mock_execution).to receive(:cwd){|*args| if args.empty? nil else expect(args.size).to eq(1) expect(args.first).to eq(@provider.release_path) end }.twice expect(mock_execution).to receive(:environment){ |*args| if args.empty? nil else expect(args.size).to eq(1) expect(args.first).to eq({"APP_ENV" => "staging"}) end }.twice @provider.run("iGoToHell4this") end it "defines run as a forwarder to execute, setting cwd and environment but not override" do mock_execution = double("Resource::Execute") expect(@provider).to receive(:execute).with("iGoToHell4this").and_return(mock_execution) @resource.user("notCoolMan") expect(mock_execution).to receive(:user).with("notCoolMan") expect(mock_execution).to receive(:cwd).with(no_args()).and_return("/some/value") expect(mock_execution).to receive(:environment).with(no_args()).and_return({}) @provider.run("iGoToHell4this") end it "converts sudo and run to exec resources in hooks" do runner = double("tehRunner") allow(Chef::Runner).to receive(:new).and_return(runner) snitch = nil @resource.user("tehCat") callback_code = Proc.new do snitch = 42 temp_collection = self.resource_collection run("tehMice") snitch = temp_collection.lookup("execute[tehMice]") end expect(runner).to receive(:converge) # @provider.callback(:phony, callback_code) expect(snitch).to be_an_instance_of(Chef::Resource::Execute) expect(snitch.user).to eq("tehCat") end end describe "installing gems from a gems.yml" do before do allow(::File).to receive(:exist?).with("#{@expected_release_dir}/gems.yml").and_return(true) @gem_list = [{:name=>"eventmachine", :version=>"0.12.9"}] end it "reads a gems.yml file, creating gem providers for each with action :upgrade" do expect(IO).to receive(:read).with("#{@expected_release_dir}/gems.yml").and_return("cookie") expect(YAML).to receive(:load).with("cookie").and_return(@gem_list) gems = @provider.send(:gem_packages) expect(gems.map { |g| g.action }).to eq([[:install]]) expect(gems.map { |g| g.name }).to eq(%w{eventmachine}) expect(gems.map { |g| g.version }).to eq(%w{0.12.9}) end it "takes a list of gem providers converges them" do allow(IO).to receive(:read) allow(YAML).to receive(:load).and_return(@gem_list) expected_gem_resources = @provider.send(:gem_packages).map { |r| [r.name, r.version] } gem_runner = @provider.send(:gem_resource_collection_runner) # no one has heard of defining == to be meaningful so I have use this monstrosity actual = gem_runner.run_context.resource_collection.all_resources.map { |r| [r.name, r.version] } expect(actual).to eq(expected_gem_resources) end end end chef-12.3.0/spec/unit/provider/cron/0000755000004100000410000000000012520074675017246 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/cron/unix_spec.rb0000644000004100000410000001105412520074675021571 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Toomas Pelberg (toomasp@gmx.net) # Copyright:: Copyright (c) 2009 Bryan McLellan # Copyright:: Copyright (c) 2010 Toomas Pelberg # 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::Cron::Unix do subject(:provider) { Chef::Provider::Cron::Unix.new(new_resource, run_context) } let(:username) { "root" } let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) do Chef::Resource::Cron.new("cronhole some stuff").tap do |resource| resource.user username resource.minute "30" resource.command "/bin/true" end end let(:status) { double('Process::Status', :exitstatus => exitstatus) } let(:exitstatus) { 0 } let(:shell_out) { double('Mixlib::ShellOut', :status => status, :stdout => stdout, :stderr => stderr) } it "is a Chef::Provider:Cron" do expect(provider).to be_a(Chef::Provider::Cron) end describe "read_crontab" do let(:stderr) { "" } let(:stdout) do String.new(<<-CRONTAB) 0 2 * * * /some/other/command # Chef Name: something else * 5 * * * /bin/true # Another comment CRONTAB end before do allow(Chef::Log).to receive(:debug) allow(shell_out).to receive(:format_for_exception).and_return("formatted command output") allow(provider).to receive(:shell_out).with('/usr/bin/crontab -l', :user => username).and_return(shell_out) end it "should call crontab -l with the user" do provider.send(:read_crontab) expect(provider).to have_received(:shell_out).with('/usr/bin/crontab -l', :user => username) end it "should return the contents of the crontab" do crontab = provider.send(:read_crontab) expect(crontab).to eq(stdout) end context "when the user has no crontab" do let(:exitstatus) { 1 } it "should return nil if the user has no crontab" do expect(provider.send(:read_crontab)).to be_nil end it "logs the crontab output to debug" do provider.send(:read_crontab) expect(Chef::Log).to have_received(:debug).with("formatted command output") end end context "when any other error occurs" do let (:exitstatus) { 2 } it "should raise an exception if another error occurs" do expect { provider.send(:read_crontab) }.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, exit: 2") end it "logs the crontab output to debug" do provider.send(:read_crontab) rescue nil expect(Chef::Log).to have_received(:debug).with("formatted command output") end end end describe "write_crontab" do let(:stdout) { "" } let(:stderr) { "" } let(:tempfile) { double("foo", :path => "/tmp/foo", :close => true) } before do expect(Tempfile).to receive(:new).and_return(tempfile) expect(tempfile).to receive(:flush) expect(tempfile).to receive(:chmod).with(420) expect(tempfile).to receive(:close!) allow(tempfile).to receive(:<<) allow(provider).to receive(:shell_out).with("/usr/bin/crontab #{tempfile.path}", :user => username).and_return(shell_out) end it "should call crontab for the user" do provider.send(:write_crontab, "Foo") expect(provider).to have_received(:shell_out).with("/usr/bin/crontab #{tempfile.path}", :user => username) end it "should call crontab with a file containing the crontab" do provider.send(:write_crontab, "Foo\n# wibble\n wah!!") expect(tempfile).to have_received(:<<).with("Foo\n# wibble\n wah!!") end context "when writing the crontab fails" do let(:exitstatus) { 1 } it "should raise an exception if the command returns non-zero" do expect { provider.send(:write_crontab, "Foo") }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/) end end end end chef-12.3.0/spec/unit/provider/registry_key_spec.rb0000644000004100000410000003003512520074675022365 0ustar www-datawww-data# # Author:: Lamont Granquist (lamont@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' shared_examples_for "a registry key" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context) @new_resource.key keyname @new_resource.values( testval1 ) @new_resource.recursive false @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context) allow(@provider).to receive(:running_on_windows!).and_return(true) @double_registry = double(Chef::Win32::Registry) allow(@provider).to receive(:registry).and_return(@double_registry) end describe "when first created" do end describe "executing load_current_resource" do describe "when the key exists" do before(:each) do expect(@double_registry).to receive(:key_exists?).with(keyname).and_return(true) expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval2 ) @provider.load_current_resource end it "should set the key of the current resource to the key of the new resource" do expect(@provider.current_resource.key).to eq(@new_resource.key) end it "should set the architecture of the current resource to the architecture of the new resource" do expect(@provider.current_resource.architecture).to eq(@new_resource.architecture) end it "should set the recursive flag of the current resource to the recursive flag of the new resource" do expect(@provider.current_resource.recursive).to eq(@new_resource.recursive) end it "should set the unscrubbed values of the current resource to the values it got from the registry" do expect(@provider.current_resource.unscrubbed_values).to eq([ testval2 ]) end end describe "when the key does not exist" do before(:each) do expect(@double_registry).to receive(:key_exists?).with(keyname).and_return(false) @provider.load_current_resource end it "should set the values in the current resource to empty array" do expect(@provider.current_resource.values).to eq([]) end end end describe "action_create" do context "when the key exists" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) end it "should do nothing if the key and the value both exist" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1 ) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create end it "should create the value if the key exists but the value does not" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval2 ) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create end it "should set the value if the key exists but the data does not match" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_data ) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create end it "should set the value if the key exists but the type does not match" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_type ) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create end end context "when the key exists and the values in the new resource are empty" do it "when a value is in the key, it should do nothing" do @provider.new_resource.values([]) expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1 ) expect(@double_registry).not_to receive(:create_key) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create end it "when no value is in the key, it should do nothing" do @provider.new_resource.values([]) expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) expect(@double_registry).to receive(:get_values).with(keyname).and_return( nil ) expect(@double_registry).not_to receive(:create_key) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create end end context "when the key does not exist" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false) end it "should create the key and the value" do expect(@double_registry).to receive(:create_key).with(keyname, false) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create end end context "when the key does not exist and the values in the new resource are empty" do it "should create the key" do @new_resource.values([]) expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false) expect(@double_registry).to receive(:create_key).with(keyname, false) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create end end end describe "action_create_if_missing" do context "when the key exists" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) end it "should do nothing if the key and the value both exist" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1 ) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create_if_missing end it "should create the value if the key exists but the value does not" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval2 ) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create_if_missing end it "should not set the value if the key exists but the data does not match" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_data ) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create_if_missing end it "should not set the value if the key exists but the type does not match" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_type ) expect(@double_registry).not_to receive(:set_value) @provider.load_current_resource @provider.action_create_if_missing end end context "when the key does not exist" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false) end it "should create the key and the value" do expect(@double_registry).to receive(:create_key).with(keyname, false) expect(@double_registry).to receive(:set_value).with(keyname, testval1) @provider.load_current_resource @provider.action_create_if_missing end end end describe "action_delete" do context "when the key exists" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) end it "deletes the value when the value exists" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1 ) expect(@double_registry).to receive(:delete_value).with(keyname, testval1) @provider.load_current_resource @provider.action_delete end it "deletes the value when the value exists, but the type is wrong" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_type ) expect(@double_registry).to receive(:delete_value).with(keyname, testval1) @provider.load_current_resource @provider.action_delete end it "deletes the value when the value exists, but the data is wrong" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1_wrong_data ) expect(@double_registry).to receive(:delete_value).with(keyname, testval1) @provider.load_current_resource @provider.action_delete end it "does not delete the value when the value does not exist" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval2 ) expect(@double_registry).not_to receive(:delete_value) @provider.load_current_resource @provider.action_delete end end context "when the key does not exist" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false) end it "does nothing" do expect(@double_registry).not_to receive(:delete_value) @provider.load_current_resource @provider.action_delete end end end describe "action_delete_key" do context "when the key exists" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true) end it "deletes the key" do expect(@double_registry).to receive(:get_values).with(keyname).and_return( testval1 ) expect(@double_registry).to receive(:delete_key).with(keyname, false) @provider.load_current_resource @provider.action_delete_key end end context "when the key does not exist" do before(:each) do expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(false) end it "does nothing" do expect(@double_registry).not_to receive(:delete_key) @provider.load_current_resource @provider.action_delete_key end end end end describe Chef::Provider::RegistryKey do context "when the key data is safe" do let(:keyname) { 'HKLM\Software\Opscode\Testing\Safe' } let(:testval1) { { :name => "one", :type => :string, :data => "1" } } let(:testval1_wrong_type) { { :name => "one", :type => :multi_string, :data => "1" } } let(:testval1_wrong_data) { { :name => "one", :type => :string, :data => "2" } } let(:testval2) { { :name => "two", :type => :string, :data => "2" } } it_should_behave_like "a registry key" end context "when the key data is unsafe" do let(:keyname) { 'HKLM\Software\Opscode\Testing\Unsafe' } let(:testval1) { { :name => "one", :type => :binary, :data => 255.chr * 1 } } let(:testval1_wrong_type) { { :name => "one", :type => :string, :data => 255.chr * 1 } } let(:testval1_wrong_data) { { :name => "one", :type => :binary, :data => 254.chr * 1 } } let(:testval2) { { :name => "two", :type => :binary, :data => 0.chr * 1 } } it_should_behave_like "a registry key" end end chef-12.3.0/spec/unit/provider/package/0000755000004100000410000000000012520074675017700 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/package/windows/0000755000004100000410000000000012520074675021372 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/package/windows/msi_spec.rb0000644000004100000410000000544112520074675023525 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Package::Windows::MSI do let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") } let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource) } before(:each) do stub_const("File::ALT_SEPARATOR", "\\") allow(::File).to receive(:absolute_path).with("calculator.msi").and_return("calculator.msi") end it "responds to shell_out!" do expect(provider).to respond_to(:shell_out!) end describe "expand_options" do it "returns an empty string if passed no options" do expect(provider.expand_options(nil)).to eql "" end it "returns a string with a leading space if passed options" do expect(provider.expand_options("--train nope --town no_way")).to eql(" --train nope --town no_way") end end describe "installed_version" do it "returns the installed version" do allow(provider).to receive(:get_product_property).and_return("{23170F69-40C1-2702-0920-000001000000}") allow(provider).to receive(:get_installed_version).with("{23170F69-40C1-2702-0920-000001000000}").and_return("3.14159.1337.42") expect(provider.installed_version).to eql("3.14159.1337.42") end end describe "package_version" do it "returns the version of a package" do allow(provider).to receive(:get_product_property).with(/calculator.msi$/, "ProductVersion").and_return(42) expect(provider.package_version).to eql(42) end end describe "install_package" do it "calls msiexec /qn /i" do expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"calculator.msi\"/, kind_of(Hash)) provider.install_package("unused", "unused") end end describe "remove_package" do it "calls msiexec /qn /x" do expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"calculator.msi\"/, kind_of(Hash)) provider.remove_package("unused", "unused") end end end chef-12.3.0/spec/unit/provider/package/windows_spec.rb0000644000004100000410000000640012520074675022731 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Package::Windows, :windows_only do let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") } let(:provider) { Chef::Provider::Package::Windows.new(new_resource, run_context) } describe "load_current_resource" do before(:each) do allow(Chef::Util::PathHelper).to receive(:validate_path) allow(provider).to receive(:package_provider).and_return(double('package_provider', :installed_version => "1.0", :package_version => "2.0")) end it "creates a current resource with the name of the new resource" do provider.load_current_resource expect(provider.current_resource).to be_a(Chef::Resource::WindowsPackage) expect(provider.current_resource.name).to eql("calculator.msi") end it "sets the current version if the package is installed" do provider.load_current_resource expect(provider.current_resource.version).to eql("1.0") end it "sets the version to be installed" do provider.load_current_resource expect(provider.new_resource.version).to eql("2.0") end it "checks that the source path is valid" do expect(Chef::Util::PathHelper).to receive(:validate_path) provider.load_current_resource end end describe "package_provider" do it "sets the package provider to MSI if the the installer type is :msi" do allow(provider).to receive(:installer_type).and_return(:msi) expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::MSI) end it "raises an error if the installer_type is unknown" do allow(provider).to receive(:installer_type).and_return(:apt_for_windows) expect { provider.package_provider }.to raise_error end end describe "installer_type" do it "it returns @installer_type if it is set" do provider.new_resource.installer_type(:downeaster) expect(provider.installer_type).to eql(:downeaster) end it "sets installer_type to msi if the source ends in .msi" do provider.new_resource.source("microsoft_installer.msi") expect(provider.installer_type).to eql(:msi) end it "raises an error if it cannot determine the installer type" do provider.new_resource.installer_type(nil) provider.new_resource.source("tomfoolery.now") expect { provider.installer_type }.to raise_error(ArgumentError) end end end chef-12.3.0/spec/unit/provider/package/aix_spec.rb0000644000004100000410000001607012520074675022024 0ustar www-datawww-data# # Author:: Deepali Jagtap (deepali.jagtap@clogeny.com) # Author:: Prabhu Das (prabhu.das@clogeny.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::Package::Aix do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("samba.base") @new_resource.source("/tmp/samba.base") @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context) allow(::File).to receive(:exists?).and_return(true) end describe "assessing the current package status" do before do @bffinfo ="/usr/lib/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX: /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:" @status = double("Status", :stdout => "", :exitstatus => 0) end it "should create a current resource with the name of new_resource" do allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource expect(@provider.current_resource.name).to eq("samba.base") end it "should set the current resource bff package name to the new resource bff package name" do allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("samba.base") end it "should raise an exception if a source is supplied but not found" do allow(@provider).to receive(:shell_out).and_return(@status) allow(::File).to receive(:exists?).and_return(false) @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Package) end it "should get the source package version from lslpp if provided" do status = double("Status", :stdout => @bffinfo, :exitstatus => 0) expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(status) expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(@status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("samba.base") expect(@new_resource.version).to eq("3.3.12.0") end it "should return the current version installed if found by lslpp" do status = double("Status", :stdout => @bffinfo, :exitstatus => 0) @stdout = StringIO.new(@bffinfo) @stdin, @stderr = StringIO.new, StringIO.new expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(@status) expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(status) @provider.load_current_resource expect(@provider.current_resource.version).to eq("3.3.12.0") end it "should raise an exception if the source is not set but we are installing" do status = double("Status", :stdout => "", :exitstatus => 1, :format_for_exception => "") @new_resource = Chef::Resource::Package.new("samba.base") @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context) allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "should raise an exception if installp/lslpp fails to run" do status = double(:stdout => "", :exitstatus => -1, :format_for_exception => "") allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should return a current resource with a nil version if the package is not found" do status = double(:stdout => "", :exitstatus => 0) expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(status) expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(status) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end end describe "candidate_version" do it "should return the candidate_version variable if already setup" do @provider.candidate_version = "3.3.12.0" expect(@provider).not_to receive(:shell_out ) @provider.candidate_version end it "should lookup the candidate_version if the variable is not already set" do status = double(:stdout => "", :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) @provider.candidate_version end it "should throw and exception if the exitstatus is not 0" do @status = double(:stdout => "", :exitstatus => 1, :format_for_exception => "") allow(@provider).to receive(:shell_out).and_return(@status) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package) end end describe "install and upgrade" do it "should run installp -aYF -d with the package source to install" do expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base samba.base") @provider.install_package("samba.base", "3.3.12.0") end it "should run when the package is a path to install" do @new_resource = Chef::Resource::Package.new("/tmp/samba.base") @provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context) expect(@new_resource.source).to eq("/tmp/samba.base") expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base /tmp/samba.base") @provider.install_package("/tmp/samba.base", "3.3.12.0") end it "should run installp with -eLogfile option." do allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log") expect(@provider).to receive(:shell_out!).with("installp -aYF -e/tmp/installp.log -d /tmp/samba.base samba.base") @provider.install_package("samba.base", "3.3.12.0") end end describe "remove" do it "should run installp -u samba.base to remove the package" do expect(@provider).to receive(:shell_out!).with("installp -u samba.base") @provider.remove_package("samba.base", "3.3.12.0") end it "should run installp -u -e/tmp/installp.log with options -e/tmp/installp.log" do allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log") expect(@provider).to receive(:shell_out!).with("installp -u -e/tmp/installp.log samba.base") @provider.remove_package("samba.base", "3.3.12.0") end end end chef-12.3.0/spec/unit/provider/package/portage_spec.rb0000644000004100000410000003351312520074675022705 0ustar www-datawww-data# # Author:: Caleb Tennis () # 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' describe Chef::Provider::Package::Portage, "load_current_resource" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("dev-util/git") @new_resource_without_category = Chef::Resource::Package.new("git") @current_resource = Chef::Resource::Package.new("dev-util/git") @provider = Chef::Provider::Package::Portage.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) end describe "when determining the current state of the package" do it "should create a current resource with the name of new_resource" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0"]) expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resource package name to the new resource package name" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0"]) expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should return a current resource with the correct version if the package is found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-foobar-0.9", "/var/db/pkg/dev-util/git-1.0.0"]) @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.0.0") end it "should return a current resource with the correct version if the package is found with revision" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0-r1"]) @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.0.0-r1") end it "should return a current resource with a nil version if the package is not found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/notgit-1.0.0"]) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end it "should return a package name match from /var/db/pkg/* if a category isn't specified and a match is found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/*/git-*").and_return(["/var/db/pkg/dev-util/git-foobar-0.9", "/var/db/pkg/dev-util/git-1.0.0"]) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.0.0") end it "should return a current resource with a nil version if a category isn't specified and a name match from /var/db/pkg/* is not found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/*/git-*").and_return(["/var/db/pkg/dev-util/notgit-1.0.0"]) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end it "should throw an exception if a category isn't specified and multiple packages are found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/*/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0", "/var/db/pkg/funny-words/git-1.0.0"]) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should return a current resource with a nil version if a category is specified and multiple packages are found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0", "/var/db/pkg/funny-words/git-1.0.0"]) @provider = Chef::Provider::Package::Portage.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end it "should return a current resource with a nil version if a category is not specified and multiple packages from the same category are found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/*/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0", "/var/db/pkg/dev-util/git-1.0.1"]) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end end describe "once the state of the package is known" do describe Chef::Provider::Package::Portage, "candidate_version" do it "should return the candidate_version variable if already set" do @provider.candidate_version = "1.0.0" expect(@provider).not_to receive(:shell_out) @provider.candidate_version end it "should throw an exception if the exitstatus is not 0" do status = double(:stdout => "", :exitstatus => 1) allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package) end it "should find the candidate_version if a category is specifed and there are no duplicates" do output = < output, :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.candidate_version).to eq("1.6.0.6") end it "should find the candidate_version if a category is not specifed and there are no duplicates" do output = < output, :exitstatus => 0) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.candidate_version).to eq("1.6.0.6") end it "should throw an exception if a category is not specified and there are duplicates" do output = < output, :exitstatus => 0) @provider = Chef::Provider::Package::Portage.new(@new_resource_without_category, @run_context) expect(@provider).to receive(:shell_out).and_return(status) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package) end it "should find the candidate_version if a category is specifed and there are category duplicates" do output = < output, :exitstatus => 0) @provider = Chef::Provider::Package::Portage.new(@new_resource, @run_context) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.candidate_version).to eq("1.6.0.6") end end describe Chef::Provider::Package::Portage, "install_package" do it "should install a normally versioned package using portage" do expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet =dev-util/git-1.0.0") @provider.install_package("dev-util/git", "1.0.0") end it "should install a tilde versioned package using portage" do expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet ~dev-util/git-1.0.0") @provider.install_package("dev-util/git", "~1.0.0") end it "should add options to the emerge command when specified" do expect(@provider).to receive(:shell_out!).with("emerge -g --color n --nospinner --quiet --oneshot =dev-util/git-1.0.0") allow(@new_resource).to receive(:options).and_return("--oneshot") @provider.install_package("dev-util/git", "1.0.0") end end describe Chef::Provider::Package::Portage, "remove_package" do it "should un-emerge the package with no version specified" do expect(@provider).to receive(:shell_out!).with("emerge --unmerge --color n --nospinner --quiet dev-util/git") @provider.remove_package("dev-util/git", nil) end it "should un-emerge the package with a version specified" do expect(@provider).to receive(:shell_out!).with("emerge --unmerge --color n --nospinner --quiet =dev-util/git-1.0.0") @provider.remove_package("dev-util/git", "1.0.0") end end end end chef-12.3.0/spec/unit/provider/package/yum_spec.rb0000644000004100000410000024475412520074675022071 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Provider::Package::Yum do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new('cups') @status = double("Status", :exitstatus => 0) @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5_2.3", :package_available? => true, :version_available? => true, :allow_multi_install => [ "kernel" ], :package_repository => "base", :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @pid = double("PID") end describe "when loading the current system state" do it "should create a current resource with the name of the new_resource" do @provider.load_current_resource expect(@provider.current_resource.name).to eq("cups") end it "should set the current resources package name to the new resources package name" do @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("cups") end it "should set the installed version to nil on the current resource if no installed package" do allow(@yum_cache).to receive(:installed_version).and_return(nil) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end it "should set the installed version if yum has one" do @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.2.4-11.18.el5") end it "should set the candidate version if yum info has one" do @provider.load_current_resource expect(@provider.candidate_version).to eql("1.2.4-11.18.el5_2.3") end it "should return the current resouce" do expect(@provider.load_current_resource).to eql(@provider.current_resource) end describe "when source is provided" do it "should set the candidate version" do @new_resource = Chef::Resource::YumPackage.new('testing.source') @new_resource.source "chef-server-core-12.0.5-1.rpm" @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) allow(File).to receive(:exists?).with(@new_resource.source).and_return(true) allow(@yum_cache).to receive(:installed_version).and_return(nil) shellout_double = double(:stdout => 'chef-server-core 12.0.5-1') allow(@provider).to receive(:shell_out!).and_return(shellout_double) @provider.load_current_resource expect(@provider.candidate_version).to eql('12.0.5-1') end end describe "when arch in package_name" do it "should set the arch if no existing package_name is found and new_package_name+new_arch is available" do @new_resource = Chef::Resource::YumPackage.new('testing.noarch') @yum_cache = double( 'Chef::Provider::Yum::YumCache' ) allow(@yum_cache).to receive(:installed_version) do |package_name, arch| # nothing installed for package_name/new_package_name nil end allow(@yum_cache).to receive(:candidate_version) do |package_name, arch| if package_name == "testing.noarch" || package_name == "testing.more.noarch" nil # candidate for new_package_name elsif package_name == "testing" || package_name == "testing.more" "1.1" end end allow(@yum_cache).to receive(:package_available?).and_return(true) allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing") expect(@provider.new_resource.arch).to eq("noarch") expect(@provider.arch).to eq("noarch") @new_resource = Chef::Resource::YumPackage.new('testing.more.noarch') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.more") expect(@provider.new_resource.arch).to eq("noarch") expect(@provider.arch).to eq("noarch") end it "should not set the arch when an existing package_name is found" do @new_resource = Chef::Resource::YumPackage.new('testing.beta3') @yum_cache = double( 'Chef::Provider::Yum::YumCache' ) allow(@yum_cache).to receive(:installed_version) do |package_name, arch| # installed for package_name if package_name == "testing.beta3" || package_name == "testing.beta3.more" "1.1" elsif package_name == "testing" || package_name == "testing.beta3" nil end end allow(@yum_cache).to receive(:candidate_version) do |package_name, arch| # no candidate for package_name/new_package_name nil end allow(@yum_cache).to receive(:package_available?).and_return(true) allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) # annoying side effect of the fun stub'ing above @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.beta3") expect(@provider.new_resource.arch).to eq(nil) expect(@provider.arch).to eq(nil) @new_resource = Chef::Resource::YumPackage.new('testing.beta3.more') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.beta3.more") expect(@provider.new_resource.arch).to eq(nil) expect(@provider.arch).to eq(nil) end it "should not set the arch when no existing package_name or new_package_name+new_arch is found" do @new_resource = Chef::Resource::YumPackage.new('testing.beta3') @yum_cache = double( 'Chef::Provider::Yum::YumCache' ) allow(@yum_cache).to receive(:installed_version) do |package_name, arch| # nothing installed for package_name/new_package_name nil end allow(@yum_cache).to receive(:candidate_version) do |package_name, arch| # no candidate for package_name/new_package_name nil end allow(@yum_cache).to receive(:package_available?).and_return(true) allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.beta3") expect(@provider.new_resource.arch).to eq(nil) expect(@provider.arch).to eq(nil) @new_resource = Chef::Resource::YumPackage.new('testing.beta3.more') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.beta3.more") expect(@provider.new_resource.arch).to eq(nil) expect(@provider.arch).to eq(nil) end it "should ensure it doesn't clobber an existing arch if passed" do @new_resource = Chef::Resource::YumPackage.new('testing.i386') @new_resource.arch("x86_64") @yum_cache = double( 'Chef::Provider::Yum::YumCache' ) allow(@yum_cache).to receive(:installed_version) do |package_name, arch| # nothing installed for package_name/new_package_name nil end allow(@yum_cache).to receive(:candidate_version) do |package_name, arch| if package_name == "testing.noarch" nil # candidate for new_package_name elsif package_name == "testing" "1.1" end end.and_return("something") allow(@yum_cache).to receive(:package_available?).and_return(true) allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider.new_resource.package_name).to eq("testing.i386") expect(@provider.new_resource.arch).to eq("x86_64") end end it "should flush the cache if :before is true" do allow(@new_resource).to receive(:flush_cache).and_return({:after => false, :before => true}) expect(@yum_cache).to receive(:reload).once @provider.load_current_resource end it "should flush the cache if :before is false" do allow(@new_resource).to receive(:flush_cache).and_return({:after => false, :before => false}) expect(@yum_cache).not_to receive(:reload) @provider.load_current_resource end it "should detect --enablerepo or --disablerepo when passed among options, collect them preserving order and notify the yum cache" do allow(@new_resource).to receive(:options).and_return("--stuff --enablerepo=foo --otherthings --disablerepo=a,b,c --enablerepo=bar") expect(@yum_cache).to receive(:enable_extra_repo_control).with("--enablerepo=foo --disablerepo=a,b,c --enablerepo=bar") @provider.load_current_resource end it "should let the yum cache know extra repos are disabled if --enablerepo or --disablerepo aren't among options" do allow(@new_resource).to receive(:options).and_return("--stuff --otherthings") expect(@yum_cache).to receive(:disable_extra_repo_control) @provider.load_current_resource end it "should let the yum cache know extra repos are disabled if options aren't set" do allow(@new_resource).to receive(:options).and_return(nil) expect(@yum_cache).to receive(:disable_extra_repo_control) @provider.load_current_resource end context "when the package name isn't found" do let(:yum_cache) { double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.0.1.el5", :candidate_version => "2.0.1.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) } before do allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(yum_cache) @pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) expect(yum_cache).to receive(:packages_from_require).and_return([@pkg]) end it "should search provides then set package_name to match" do @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq('test-package') expect(@new_resource.version).to eq(nil) end it "should search provides then set version to match if a requirement was passed in the package name" do @new_resource = Chef::Resource::YumPackage.new('test-package = 2.0.1.el5') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq('test-package') expect(@new_resource.version).to eq('2.0.1.el5') end it "should search provides then set version to match if a requirement was passed in the version" do @new_resource = Chef::Resource::YumPackage.new('test-package') @new_resource.version('= 2.0.1.el5') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq('test-package') expect(@new_resource.version).to eq('2.0.1.el5') end it "should search provides and not set the version to match if a specific version was requested" do @new_resource = Chef::Resource::YumPackage.new('test-package') @new_resource.version('3.0.1.el5') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq('test-package') expect(@new_resource.version).to eq('3.0.1.el5') end it "should search provides then set versions to match if requirements were passed in the package name as an array" do @new_resource = Chef::Resource::YumPackage.new(['test-package = 2.0.1.el5']) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq(['test-package']) expect(@new_resource.version).to eq(['2.0.1.el5']) end it "should search provides and not set the versions to match if specific versions were requested in an array" do @new_resource = Chef::Resource::YumPackage.new(['test-package']) @new_resource.version(['3.0.1.el5']) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq(['test-package']) expect(@new_resource.version).to eq(['3.0.1.el5']) end end it "should not return an error if no version number is specified in the resource" do @new_resource = Chef::Resource::YumPackage.new('test-package') @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.0.1.el5", :candidate_version => "2.0.1.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) expect(@yum_cache).to receive(:packages_from_require).and_return([pkg]) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq("test-package") expect(@new_resource.version).to eq(nil) end it "should give precedence to the version attribute when both a requirement in the resource name and a version attribute are specified" do @new_resource = Chef::Resource::YumPackage.new('test-package') @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) expect(@yum_cache).to receive(:packages_from_require).and_return([pkg]) @new_resource = Chef::Resource::YumPackage.new('test-package = 2.0.1.el5') @new_resource.version('3.0.1.el5') @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq('test-package') expect(@new_resource.version).to eq('3.0.1.el5') end it "should correctly detect the installed states of an array of package names and version numbers" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.0.1.el5", :candidate_version => "2.0.1.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) expect(@yum_cache).to receive(:packages_from_require).exactly(4).times.and_return([]) expect(@yum_cache).to receive(:reload_provides).twice @new_resource = Chef::Resource::YumPackage.new(['test-package','test-package2']) @new_resource.version(['2.0.1.el5','3.0.1.el5']) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq(['test-package','test-package2']) expect(@new_resource.version).to eq(['2.0.1.el5','3.0.1.el5']) end it "should search provides if no package is available - if no match in installed provides then load the complete set" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) expect(@yum_cache).to receive(:packages_from_require).twice.and_return([]) expect(@yum_cache).to receive(:reload_provides) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.version).to eq(nil) end it "should search provides if no package is available and not load the complete set if action is :remove or :purge" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) expect(@yum_cache).to receive(:packages_from_require).once.and_return([]) expect(@yum_cache).not_to receive(:reload_provides) @new_resource.action(:remove) @provider.load_current_resource expect(@yum_cache).to receive(:packages_from_require).once.and_return([]) expect(@yum_cache).not_to receive(:reload_provides) @new_resource.action(:purge) @provider.load_current_resource end it "should search provides if no package is available - if no match in provides leave the name intact" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_provides => true, :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5", :package_available? => false, :version_available? => true, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) expect(@yum_cache).to receive(:packages_from_require).twice.and_return([]) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq("cups") end end describe "when installing a package" do it "should run yum install with the package name and version" do @provider.load_current_resource allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.19.el5" ) @provider.install_package("cups", "1.2.4-11.19.el5") end it "should run yum localinstall if given a path to an rpm" do allow(@new_resource).to receive(:source).and_return("/tmp/emacs-21.4-20.el5.i386.rpm") expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm" ) @provider.install_package("emacs", "21.4-20.el5") end it "should run yum localinstall if given a path to an rpm as the package" do @new_resource = Chef::Resource::Package.new("/tmp/emacs-21.4-20.el5.i386.rpm") allow(::File).to receive(:exists?).and_return(true) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) expect(@new_resource.source).to eq("/tmp/emacs-21.4-20.el5.i386.rpm") expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm" ) @provider.install_package("/tmp/emacs-21.4-20.el5.i386.rpm", "21.4-20.el5") end it "should run yum install with the package name, version and arch" do @provider.load_current_resource allow(@new_resource).to receive(:arch).and_return("i386") allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.19.el5.i386" ) @provider.install_package("cups", "1.2.4-11.19.el5") end it "installs the package with the options given in the resource" do @provider.load_current_resource allow(@provider).to receive(:candidate_version).and_return('11') allow(@new_resource).to receive(:options).and_return("--disablerepo epmd") allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y --disablerepo epmd install cups-11" ) @provider.install_package(@new_resource.name, @provider.candidate_version) end it "should raise an exception if the package is not available" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_from_cache => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.18.el5_2.3", :package_available? => true, :version_available? => nil, :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) expect { @provider.install_package("lolcats", "0.99") }.to raise_error(Chef::Exceptions::Package, %r{Version .* not found}) end it "should raise an exception if candidate version is older than the installed version and allow_downgrade is false" do allow(@new_resource).to receive(:allow_downgrade).and_return(false) @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.15.el5", :package_available? => true, :version_available? => true, :allow_multi_install => [ "kernel" ], :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect { @provider.install_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package}) end it "should not raise an exception if candidate version is older than the installed version and the package is list in yum's installonlypkg option" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.15.el5", :package_available? => true, :version_available? => true, :allow_multi_install => [ "cups" ], :package_repository => "base", :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.15.el5" ) @provider.install_package("cups", "1.2.4-11.15.el5") end it "should run yum downgrade if candidate version is older than the installed version and allow_downgrade is true" do allow(@new_resource).to receive(:allow_downgrade).and_return(true) @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.15.el5", :package_available? => true, :version_available? => true, :allow_multi_install => [], :package_repository => "base", :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y downgrade cups-1.2.4-11.15.el5" ) @provider.install_package("cups", "1.2.4-11.15.el5") end it "should run yum install then flush the cache if :after is true" do allow(@new_resource).to receive(:flush_cache).and_return({:after => true, :before => false}) @provider.load_current_resource allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.15.el5" ) expect(@yum_cache).to receive(:reload).once @provider.install_package("cups", "1.2.4-11.15.el5") end it "should run yum install then not flush the cache if :after is false" do allow(@new_resource).to receive(:flush_cache).and_return({:after => false, :before => false}) @provider.load_current_resource allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.15.el5" ) expect(@yum_cache).not_to receive(:reload) @provider.install_package("cups", "1.2.4-11.15.el5") end end describe "when upgrading a package" do it "should run yum install if the package is installed and a version is given" do @provider.load_current_resource allow(@provider).to receive(:candidate_version).and_return('11') allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-11" ) @provider.upgrade_package(@new_resource.name, @provider.candidate_version) end it "should run yum install if the package is not installed" do @provider.load_current_resource @current_resource = Chef::Resource::Package.new('cups') allow(@provider).to receive(:candidate_version).and_return('11') allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-11" ) @provider.upgrade_package(@new_resource.name, @provider.candidate_version) end it "should raise an exception if candidate version is older than the installed version" do @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => "1.2.4-11.18.el5", :candidate_version => "1.2.4-11.15.el5", :package_available? => true, :version_available? => true, :allow_multi_install => [ "kernel" ], :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect { @provider.upgrade_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package}) end # Test our little workaround, some crossover into Chef::Provider::Package territory it "should call action_upgrade in the parent if the current resource version is nil" do allow(@yum_cache).to receive(:installed_version).and_return(nil) @current_resource = Chef::Resource::Package.new('cups') allow(@provider).to receive(:candidate_version).and_return('11') expect(@provider).to receive(:upgrade_package).with( "cups", "11" ) @provider.run_action(:upgrade) end it "should call action_upgrade in the parent if the candidate version is nil" do @provider.load_current_resource @current_resource = Chef::Resource::Package.new('cups') allow(@provider).to receive(:candidate_version).and_return(nil) expect(@provider).not_to receive(:upgrade_package) @provider.run_action(:upgrade) end it "should call action_upgrade in the parent if the candidate is newer" do @provider.load_current_resource @current_resource = Chef::Resource::Package.new('cups') allow(@provider).to receive(:candidate_version).and_return('11') expect(@provider).to receive(:upgrade_package).with( "cups", "11" ) @provider.run_action(:upgrade) end it "should not call action_upgrade in the parent if the candidate is older" do allow(@yum_cache).to receive(:installed_version).and_return("12") @provider.load_current_resource @current_resource = Chef::Resource::Package.new('cups') allow(@provider).to receive(:candidate_version).and_return('11') expect(@provider).not_to receive(:upgrade_package) @provider.run_action(:upgrade) end end describe "when removing a package" do it "should run yum remove with the package name" do expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y remove emacs-1.0" ) @provider.remove_package("emacs", "1.0") end it "should run yum remove with the package name and arch" do allow(@new_resource).to receive(:arch).and_return("x86_64") expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y remove emacs-1.0.x86_64" ) @provider.remove_package("emacs", "1.0") end end describe "when purging a package" do it "should run yum remove with the package name" do expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y remove emacs-1.0" ) @provider.purge_package("emacs", "1.0") end end describe "when running yum" do it "should run yum once if it exits with a return code of 0" do @status = double("Status", :exitstatus => 0, :stdout => "", :stderr => "") allow(@provider).to receive(:shell_out).and_return(@status) expect(@provider).to receive(:shell_out).once.with( "yum -d0 -e0 -y install emacs-1.0", {:timeout => Chef::Config[:yum_timeout]} ) @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") end it "should run yum once if it exits with a return code > 0 and no scriptlet failures" do @status = double("Status", :exitstatus => 2, :stdout => "failure failure", :stderr => "problem problem") allow(@provider).to receive(:shell_out).and_return(@status) expect(@provider).to receive(:shell_out).once.with( "yum -d0 -e0 -y install emacs-1.0", {:timeout => Chef::Config[:yum_timeout]} ) expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec) end it "should run yum once if it exits with a return code of 1 and %pre scriptlet failures" do @status = double("Status", :exitstatus => 1, :stdout => "error: %pre(demo-1-1.el5.centos.x86_64) scriptlet failed, exit status 2", :stderr => "") allow(@provider).to receive(:shell_out).and_return(@status) expect(@provider).to receive(:shell_out).once.with( "yum -d0 -e0 -y install emacs-1.0", {:timeout => Chef::Config[:yum_timeout]} ) # will still raise an exception, can't stub out the subsequent call expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec) end it "should run yum twice if it exits with a return code of 1 and %post scriptlet failures" do @status = double("Status", :exitstatus => 1, :stdout => "error: %post(demo-1-1.el5.centos.x86_64) scriptlet failed, exit status 2", :stderr => "") allow(@provider).to receive(:shell_out).and_return(@status) expect(@provider).to receive(:shell_out).twice.with( "yum -d0 -e0 -y install emacs-1.0", {:timeout => Chef::Config[:yum_timeout]} ) # will still raise an exception, can't stub out the subsequent call expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec) end end end describe Chef::Provider::Package::Yum::RPMUtils do describe "version_parse" do before do @rpmutils = Chef::Provider::Package::Yum::RPMUtils end it "parses known good epoch strings" do [ [ "0:3.3", [ 0, "3.3", nil ] ], [ "9:1.7.3", [ 9, "1.7.3", nil ] ], [ "15:20020927", [ 15, "20020927", nil ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end it "parses strange epoch strings" do [ [ ":3.3", [ 0, "3.3", nil ] ], [ "-1:1.7.3", [ nil, nil, "1:1.7.3" ] ], [ "-:20020927", [ nil, nil, ":20020927" ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end it "parses known good version strings" do [ [ "3.3", [ nil, "3.3", nil ] ], [ "1.7.3", [ nil, "1.7.3", nil ] ], [ "20020927", [ nil, "20020927", nil ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end it "parses strange version strings" do [ [ "3..3", [ nil, "3..3", nil ] ], [ "0001.7.3", [ nil, "0001.7.3", nil ] ], [ "20020927,3", [ nil, "20020927,3", nil ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end it "parses known good version release strings" do [ [ "3.3-0.pre3.1.60.el5_5.1", [ nil, "3.3", "0.pre3.1.60.el5_5.1" ] ], [ "1.7.3-1jpp.2.el5", [ nil, "1.7.3", "1jpp.2.el5" ] ], [ "20020927-46.el5", [ nil, "20020927", "46.el5" ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end it "parses strange version release strings" do [ [ "3.3-", [ nil, "3.3", nil ] ], [ "-1jpp.2.el5", [ nil, nil, "1jpp.2.el5" ] ], [ "-0020020927-46.el5", [ nil, "-0020020927", "46.el5" ] ] ].each do |x, y| expect(@rpmutils.version_parse(x)).to eq(y) end end end describe "rpmvercmp" do before do @rpmutils = Chef::Provider::Package::Yum::RPMUtils end it "should validate version compare logic for standard examples" do [ # numeric [ "0.0.2", "0.0.1", 1 ], [ "0.2.0", "0.1.0", 1 ], [ "2.0.0", "1.0.0", 1 ], [ "0.0.1", "0.0.1", 0 ], [ "0.0.1", "0.0.2", -1 ], [ "0.1.0", "0.2.0", -1 ], [ "1.0.0", "2.0.0", -1 ], # alpha [ "bb", "aa", 1 ], [ "ab", "aa", 1 ], [ "aa", "aa", 0 ], [ "aa", "bb", -1 ], [ "aa", "ab", -1 ], [ "BB", "AA", 1 ], [ "AA", "AA", 0 ], [ "AA", "BB", -1 ], [ "aa", "AA", 1 ], [ "AA", "aa", -1 ], # alphanumeric [ "0.0.1b", "0.0.1a", 1 ], [ "0.1b.0", "0.1a.0", 1 ], [ "1b.0.0", "1a.0.0", 1 ], [ "0.0.1a", "0.0.1a", 0 ], [ "0.0.1a", "0.0.1b", -1 ], [ "0.1a.0", "0.1b.0", -1 ], [ "1a.0.0", "1b.0.0", -1 ], # alphanumeric against alphanumeric [ "0.0.1", "0.0.a", 1 ], [ "0.1.0", "0.a.0", 1 ], [ "1.0.0", "a.0.0", 1 ], [ "0.0.a", "0.0.a", 0 ], [ "0.0.a", "0.0.1", -1 ], [ "0.a.0", "0.1.0", -1 ], [ "a.0.0", "1.0.0", -1 ], # alphanumeric against numeric [ "0.0.2", "0.0.1a", 1 ], [ "0.0.2a", "0.0.1", 1 ], [ "0.0.1", "0.0.2a", -1 ], [ "0.0.1a", "0.0.2", -1 ], # length [ "0.0.1aa", "0.0.1a", 1 ], [ "0.0.1aa", "0.0.1aa", 0 ], [ "0.0.1a", "0.0.1aa", -1 ], ].each do |x, y, result| expect(@rpmutils.rpmvercmp(x,y)).to eq(result) end end it "should validate version compare logic for strange examples" do [ [ "2,0,0", "1.0.0", 1 ], [ "0.0.1", "0,0.1", 0 ], [ "1.0.0", "2,0,0", -1 ], [ "002.0.0", "001.0.0", 1 ], [ "001..0.1", "001..0.0", 1 ], [ "-001..1", "-001..0", 1 ], [ "1.0.1", nil, 1 ], [ nil, nil, 0 ], [ nil, "1.0.1", -1 ], [ "1.0.1", "", 1 ], [ "", "", 0 ], [ "", "1.0.1", -1 ] ].each do |x, y, result| expect(@rpmutils.rpmvercmp(x,y)).to eq(result) end end it "tests isalnum good input" do [ 'a', 'z', 'A', 'Z', '0', '9' ].each do |t| expect(@rpmutils.isalnum(t)).to eq(true) end end it "tests isalnum bad input" do [ '-', '.', '!', '^', ':', '_' ].each do |t| expect(@rpmutils.isalnum(t)).to eq(false) end end it "tests isalpha good input" do [ 'a', 'z', 'A', 'Z', ].each do |t| expect(@rpmutils.isalpha(t)).to eq(true) end end it "tests isalpha bad input" do [ '0', '9', '-', '.', '!', '^', ':', '_' ].each do |t| expect(@rpmutils.isalpha(t)).to eq(false) end end it "tests isdigit good input" do [ '0', '9', ].each do |t| expect(@rpmutils.isdigit(t)).to eq(true) end end it "tests isdigit bad input" do [ 'A', 'z', '-', '.', '!', '^', ':', '_' ].each do |t| expect(@rpmutils.isdigit(t)).to eq(false) end end end end describe Chef::Provider::Package::Yum::RPMVersion do describe "new - with parsing" do before do @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5") end it "should expose evr (name-version-release) available" do expect(@rpmv.e).to eq(1) expect(@rpmv.v).to eq("1.6.5") expect(@rpmv.r).to eq("9.36.el5") expect(@rpmv.evr).to eq("1:1.6.5-9.36.el5") end it "should output a version-release string" do expect(@rpmv.to_s).to eq("1.6.5-9.36.el5") end end describe "new - no parsing" do before do @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5") end it "should expose evr (name-version-release) available" do expect(@rpmv.e).to eq(1) expect(@rpmv.v).to eq("1.6.5") expect(@rpmv.r).to eq("9.36.el5") expect(@rpmv.evr).to eq("1:1.6.5-9.36.el5") end it "should output a version-release string" do expect(@rpmv.to_s).to eq("1.6.5-9.36.el5") end end it "should raise an error unless passed 1 or 3 args" do expect { Chef::Provider::Package::Yum::RPMVersion.new() }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5") }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5", "extra") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5") }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5", "extra") }.to raise_error(ArgumentError) end # thanks version_class_spec.rb! describe "compare" do it "should sort based on complete epoch-version-release data" do [ # smaller, larger [ "0:1.6.5-9.36.el5", "1:1.6.5-9.36.el5" ], [ "0:2.3-15.el5", "0:3.3-15.el5" ], [ "0:alpha9.8-27.2", "0:beta9.8-27.2" ], [ "0:0.09-14jpp.3", "0:0.09-15jpp.3" ], [ "0:0.9.0-0.6.20110211.el5", "0:0.9.0-0.6.20120211.el5" ], [ "0:1.9.1-4.el5", "0:1.9.1-5.el5" ], [ "0:1.4.10-7.20090624svn.el5", "0:1.4.10-7.20090625svn.el5" ], [ "0:2.3.4-2.el5", "0:2.3.4-2.el6" ] ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end it "should sort based on partial epoch-version-release data" do [ # smaller, larger [ ":1.6.5-9.36.el5", "1:1.6.5-9.36.el5" ], [ "2.3-15.el5", "3.3-15.el5" ], [ "alpha9.8", "beta9.8" ], [ "14jpp", "15jpp" ], [ "0.9.0-0.6", "0.9.0-0.7" ], [ "0:1.9", "3:1.9" ], [ "2.3-2.el5", "2.3-2.el6" ] ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end it "should verify equality of complete epoch-version-release data" do [ [ "0:1.6.5-9.36.el5", "0:1.6.5-9.36.el5" ], [ "0:2.3-15.el5", "0:2.3-15.el5" ], [ "0:alpha9.8-27.2", "0:alpha9.8-27.2" ] ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm).to eq(lg) end end it "should verify equality of partial epoch-version-release data" do [ [ ":1.6.5-9.36.el5", "0:1.6.5-9.36.el5" ], [ "2.3-15.el5", "2.3-15.el5" ], [ "alpha9.8-3", "alpha9.8-3" ] ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm).to eq(lg) end end end describe "partial compare" do it "should compare based on partial epoch-version-release data" do [ # smaller, larger [ "0:1.1.1-1", "1:" ], [ "0:1.1.1-1", "0:1.1.2" ], [ "0:1.1.1-1", "0:1.1.2-1" ], [ "0:", "1:1.1.1-1" ], [ "0:1.1.1", "0:1.1.2-1" ], [ "0:1.1.1-1", "0:1.1.2-1" ], ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm.partial_compare(lg)).to eq(-1) expect(lg.partial_compare(sm)).to eq(1) expect(sm.partial_compare(lg)).not_to eq(0) end end it "should verify equality based on partial epoch-version-release data" do [ [ "0:", "0:1.1.1-1" ], [ "0:1.1.1", "0:1.1.1-1" ], [ "0:1.1.1-1", "0:1.1.1-1" ], ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller) lg = Chef::Provider::Package::Yum::RPMVersion.new(larger) expect(sm.partial_compare(lg)).to eq(0) end end end end describe Chef::Provider::Package::Yum::RPMPackage do describe "new - with parsing" do before do @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", []) end it "should expose nevra (name-epoch-version-release-arch) available" do expect(@rpm.name).to eq("testing") expect(@rpm.version.e).to eq(1) expect(@rpm.version.v).to eq("1.6.5") expect(@rpm.version.r).to eq("9.36.el5") expect(@rpm.arch).to eq("x86_64") expect(@rpm.nevra).to eq("testing-1:1.6.5-9.36.el5.x86_64") expect(@rpm.to_s).to eq(@rpm.nevra) end it "should always have at least one provide, itself" do expect(@rpm.provides.size).to eq(1) @rpm.provides[0].name == "testing" @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5" @rpm.provides[0].flag == :== end end describe "new - no parsing" do before do @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", []) end it "should expose nevra (name-epoch-version-release-arch) available" do expect(@rpm.name).to eq("testing") expect(@rpm.version.e).to eq(1) expect(@rpm.version.v).to eq("1.6.5") expect(@rpm.version.r).to eq("9.36.el5") expect(@rpm.arch).to eq("x86_64") expect(@rpm.nevra).to eq("testing-1:1.6.5-9.36.el5.x86_64") expect(@rpm.to_s).to eq(@rpm.nevra) end it "should always have at least one provide, itself" do expect(@rpm.provides.size).to eq(1) @rpm.provides[0].name == "testing" @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5" @rpm.provides[0].flag == :== end end it "should raise an error unless passed 4 or 6 args" do expect { Chef::Provider::Package::Yum::RPMPackage.new() }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMPackage.new("testing") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", []) }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", []) }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [], "extra") }.to raise_error(ArgumentError) end describe "<=>" do it "should sort alphabetically based on package name" do [ [ "a-test", "b-test" ], [ "B-test", "a-test" ], [ "A-test", "B-test" ], [ "Aa-test", "aA-test" ], [ "1test", "2test" ], ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMPackage.new(smaller, "0:0.0.1-1", "x86_64", []) lg = Chef::Provider::Package::Yum::RPMPackage.new(larger, "0:0.0.1-1", "x86_64", []) expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end it "should sort alphabetically based on package arch" do [ [ "i386", "x86_64" ], [ "i386", "noarch" ], [ "noarch", "x86_64" ], ].each do |smaller, larger| sm = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", smaller, []) lg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", larger, []) expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end end end describe Chef::Provider::Package::Yum::RPMDbPackage do before(:each) do # name, version, arch, installed, available, repoid @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], false, true, "base") @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, true, "extras") @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, false, "other") end describe "initialize" do it "should return a Chef::Provider::Package::Yum::RPMDbPackage object" do expect(@rpm_x).to be_kind_of(Chef::Provider::Package::Yum::RPMDbPackage) end end describe "available" do it "should return true" do expect(@rpm_x.available).to eq(true) expect(@rpm_y.available).to eq(true) expect(@rpm_z.available).to eq(false) end end describe "installed" do it "should return true" do expect(@rpm_x.installed).to eq(false) expect(@rpm_y.installed).to eq(true) expect(@rpm_z.installed).to eq(true) end end describe "repoid" do it "should return the source repository repoid" do expect(@rpm_x.repoid).to eq("base") expect(@rpm_y.repoid).to eq("extras") expect(@rpm_z.repoid).to eq("other") end end end describe Chef::Provider::Package::Yum::RPMDependency do describe "new - with parsing" do before do @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==) end it "should expose name, version, flag available" do expect(@rpmdep.name).to eq("testing") expect(@rpmdep.version.e).to eq(1) expect(@rpmdep.version.v).to eq("1.6.5") expect(@rpmdep.version.r).to eq("9.36.el5") expect(@rpmdep.flag).to eq(:==) end end describe "new - no parsing" do before do @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==) end it "should expose name, version, flag available" do expect(@rpmdep.name).to eq("testing") expect(@rpmdep.version.e).to eq(1) expect(@rpmdep.version.v).to eq("1.6.5") expect(@rpmdep.version.r).to eq("9.36.el5") expect(@rpmdep.flag).to eq(:==) end end it "should raise an error unless passed 3 or 5 args" do expect { Chef::Provider::Package::Yum::RPMDependency.new() }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMDependency.new("testing") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==) }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==, "extra") }.to raise_error(ArgumentError) expect { Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==) }.not_to raise_error expect { Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==, "extra") }.to raise_error(ArgumentError) end describe "parse" do it "should parse a name, flag, version string into a valid RPMDependency object" do @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing >= 1:1.6.5-9.36.el5") expect(@rpmdep.name).to eq("testing") expect(@rpmdep.version.e).to eq(1) expect(@rpmdep.version.v).to eq("1.6.5") expect(@rpmdep.version.r).to eq("9.36.el5") expect(@rpmdep.flag).to eq(:>=) end it "should parse a name into a valid RPMDependency object" do @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing") expect(@rpmdep.name).to eq("testing") expect(@rpmdep.version.e).to eq(nil) expect(@rpmdep.version.v).to eq(nil) expect(@rpmdep.version.r).to eq(nil) expect(@rpmdep.flag).to eq(:==) end it "should parse an invalid string into the name of a RPMDependency object" do @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing blah >") expect(@rpmdep.name).to eq("testing blah >") expect(@rpmdep.version.e).to eq(nil) expect(@rpmdep.version.v).to eq(nil) expect(@rpmdep.version.r).to eq(nil) expect(@rpmdep.flag).to eq(:==) end it "should parse various valid flags" do [ [ ">", :> ], [ ">=", :>= ], [ "=", :== ], [ "==", :== ], [ "<=", :<= ], [ "<", :< ] ].each do |before, after| @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1") expect(@rpmdep.flag).to eq(after) end end it "should parse various invalid flags and treat them as names" do [ [ "<>", :== ], [ "!=", :== ], [ ">>", :== ], [ "<<", :== ], [ "!", :== ], [ "~", :== ] ].each do |before, after| @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1") expect(@rpmdep.name).to eq("testing #{before} 1:1.1-1") expect(@rpmdep.flag).to eq(after) end end end describe "satisfy?" do it "should raise an error unless a RPMDependency is passed" do @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==) @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=) expect { @rpmprovide.satisfy?("hi") }.to raise_error(ArgumentError) expect { @rpmprovide.satisfy?(@rpmrequire) }.not_to raise_error end it "should validate dependency satisfaction logic for standard examples" do [ # names [ "test", "test", true ], [ "test", "foo", false ], # full: epoch:version-relese [ "testing = 1:1.1-1", "testing > 1:1.1-0", true ], [ "testing = 1:1.1-1", "testing >= 1:1.1-0", true ], [ "testing = 1:1.1-1", "testing >= 1:1.1-1", true ], [ "testing = 1:1.1-1", "testing = 1:1.1-1", true ], [ "testing = 1:1.1-1", "testing == 1:1.1-1", true ], [ "testing = 1:1.1-1", "testing <= 1:1.1-1", true ], [ "testing = 1:1.1-1", "testing <= 1:1.1-0", false ], [ "testing = 1:1.1-1", "testing < 1:1.1-0", false ], # partial: epoch:version [ "testing = 1:1.1", "testing > 1:1.0", true ], [ "testing = 1:1.1", "testing >= 1:1.0", true ], [ "testing = 1:1.1", "testing >= 1:1.1", true ], [ "testing = 1:1.1", "testing = 1:1.1", true ], [ "testing = 1:1.1", "testing == 1:1.1", true ], [ "testing = 1:1.1", "testing <= 1:1.1", true ], [ "testing = 1:1.1", "testing <= 1:1.0", false ], [ "testing = 1:1.1", "testing < 1:1.0", false ], # partial: epoch [ "testing = 1:", "testing > 0:", true ], [ "testing = 1:", "testing >= 0:", true ], [ "testing = 1:", "testing >= 1:", true ], [ "testing = 1:", "testing = 1:", true ], [ "testing = 1:", "testing == 1:", true ], [ "testing = 1:", "testing <= 1:", true ], [ "testing = 1:", "testing <= 0:", false ], [ "testing = 1:", "testing < 0:", false ], # mix and match! [ "testing = 1:1.1-1", "testing == 1:1.1", true ], [ "testing = 1:1.1-1", "testing == 1:", true ], ].each do |prov, req, result| @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.parse(prov) @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse(req) expect(@rpmprovide.satisfy?(@rpmrequire)).to eq(result) expect(@rpmrequire.satisfy?(@rpmprovide)).to eq(result) end end end end # thanks resource_collection_spec.rb! describe Chef::Provider::Package::Yum::RPMDb do before(:each) do @rpmdb = Chef::Provider::Package::Yum::RPMDb.new # name, version, arch, installed, available deps_v = [ Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"), Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a = 0:1.6.5-9.36.el5") ] deps_z = [ Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"), Chef::Provider::Package::Yum::RPMDependency.parse("config(test) = 0:1.6.5-9.36.el5"), Chef::Provider::Package::Yum::RPMDependency.parse("test-package-c = 0:1.6.5-9.36.el5") ] @rpm_v = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-a", "0:1.6.5-9.36.el5", "i386", deps_v, true, false, "base") @rpm_w = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "i386", [], true, true, "extras") @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "x86_64", [], false, true, "extras") @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "1:1.6.5-9.36.el5", "x86_64", [], true, true, "extras") @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true, "base") @rpm_z_mirror = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true, "base") end describe "initialize" do it "should return a Chef::Provider::Package::Yum::RPMDb object" do expect(@rpmdb).to be_kind_of(Chef::Provider::Package::Yum::RPMDb) end end describe "push" do it "should accept an RPMDbPackage object through pushing" do expect { @rpmdb.push(@rpm_w) }.not_to raise_error end it "should accept multiple RPMDbPackage object through pushing" do expect { @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) }.not_to raise_error end it "should only accept an RPMDbPackage object" do expect { @rpmdb.push("string") }.to raise_error end it "should add the package to the package db" do @rpmdb.push(@rpm_w) expect(@rpmdb["test-package-b"]).not_to eq(nil) end it "should add conditionally add the package to the available list" do expect(@rpmdb.available_size).to eq(0) @rpmdb.push(@rpm_v, @rpm_w) expect(@rpmdb.available_size).to eq(1) end it "should add conditionally add the package to the installed list" do expect(@rpmdb.installed_size).to eq(0) @rpmdb.push(@rpm_w, @rpm_x) expect(@rpmdb.installed_size).to eq(1) end it "should have a total of 2 packages in the RPMDb" do expect(@rpmdb.size).to eq(0) @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb.size).to eq(2) end it "should keep the Array unique when a duplicate is pushed" do @rpmdb.push(@rpm_z, @rpm_z_mirror) expect(@rpmdb["test-package-c"].size).to eq(1) end it "should register the package provides in the provides index" do @rpmdb.push(@rpm_v, @rpm_w, @rpm_z) expect(@rpmdb.lookup_provides("test-package-a")[0]).to eq(@rpm_v) expect(@rpmdb.lookup_provides("config(test)")[0]).to eq(@rpm_z) expect(@rpmdb.lookup_provides("libz.so.1()(64bit)")[0]).to eq(@rpm_v) expect(@rpmdb.lookup_provides("libz.so.1()(64bit)")[1]).to eq(@rpm_z) end end describe "<<" do it "should accept an RPMPackage object through the << operator" do expect { @rpmdb << @rpm_w }.not_to raise_error end end describe "lookup" do it "should return an Array of RPMPackage objects by index" do @rpmdb << @rpm_w expect(@rpmdb.lookup("test-package-b")).to be_kind_of(Array) end end describe "[]" do it "should return an Array of RPMPackage objects though the [index] operator" do @rpmdb << @rpm_w expect(@rpmdb["test-package-b"]).to be_kind_of(Array) end it "should return an Array of 3 RPMPackage objects" do @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb["test-package-b"].size).to eq(3) end it "should return an Array of RPMPackage objects sorted from newest to oldest" do @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb["test-package-b"][0]).to eq(@rpm_y) expect(@rpmdb["test-package-b"][1]).to eq(@rpm_x) expect(@rpmdb["test-package-b"][2]).to eq(@rpm_w) end end describe "lookup_provides" do it "should return an Array of RPMPackage objects by index" do @rpmdb << @rpm_z x = @rpmdb.lookup_provides("config(test)") expect(x).to be_kind_of(Array) expect(x[0]).to eq(@rpm_z) end end describe "clear" do it "should clear the RPMDb" do expect(@rpmdb).to receive(:clear_available).once expect(@rpmdb).to receive(:clear_installed).once @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb.size).not_to eq(0) expect(@rpmdb.lookup_provides("config(test)")).to be_kind_of(Array) @rpmdb.clear expect(@rpmdb.lookup_provides("config(test)")).to eq(nil) expect(@rpmdb.size).to eq(0) end end describe "clear_available" do it "should clear the available list" do @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb.available_size).not_to eq(0) @rpmdb.clear_available expect(@rpmdb.available_size).to eq(0) end end describe "available?" do it "should return true if a package is available" do expect(@rpmdb.available?(@rpm_w)).to eq(false) @rpmdb.push(@rpm_v, @rpm_w) expect(@rpmdb.available?(@rpm_v)).to eq(false) expect(@rpmdb.available?(@rpm_w)).to eq(true) end end describe "clear_installed" do it "should clear the installed list" do @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) expect(@rpmdb.installed_size).not_to eq(0) @rpmdb.clear_installed expect(@rpmdb.installed_size).to eq(0) end end describe "installed?" do it "should return true if a package is installed" do expect(@rpmdb.installed?(@rpm_w)).to eq(false) @rpmdb.push(@rpm_w, @rpm_x) expect(@rpmdb.installed?(@rpm_w)).to eq(true) expect(@rpmdb.installed?(@rpm_x)).to eq(false) end end describe "whatprovides" do it "should raise an error unless a RPMDependency is passed" do @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==) @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=) expect { @rpmdb.whatprovides("hi") }.to raise_error(ArgumentError) expect { @rpmdb.whatprovides(@rpmrequire) }.not_to raise_error end it "should return an Array of packages statisfying a RPMDependency" do @rpmdb.push(@rpm_v, @rpm_w, @rpm_z) @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a >= 1.6.5") x = @rpmdb.whatprovides(@rpmrequire) expect(x).to be_kind_of(Array) expect(x[0]).to eq(@rpm_v) @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)") x = @rpmdb.whatprovides(@rpmrequire) expect(x).to be_kind_of(Array) expect(x[0]).to eq(@rpm_v) expect(x[1]).to eq(@rpm_z) end end end describe Chef::Provider::Package::Yum::YumCache do # allow for the reset of a Singleton # thanks to Ian White (http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby) class << Chef::Provider::Package::Yum::YumCache def reset_instance Singleton.send :__init__, self self end end before(:each) do @stdin = double("STDIN", :nil_object => true) @stdout = double("STDOUT", :nil_object => true) @stdout_good = < 0, :stdin => @stdin, :stdout => @stdout_good, :stderr => @stderr) # new singleton each time Chef::Provider::Package::Yum::YumCache.reset_instance @yc = Chef::Provider::Package::Yum::YumCache.instance # load valid data allow(@yc).to receive(:shell_out!).and_return(@status) end describe "initialize" do it "should return a Chef::Provider::Package::Yum::YumCache object" do expect(@yc).to be_kind_of(Chef::Provider::Package::Yum::YumCache) end it "should register reload for start of Chef::Client runs" do Chef::Provider::Package::Yum::YumCache.reset_instance expect(Chef::Client).to receive(:when_run_starts) do |&b| expect(b).not_to be_nil end @yc = Chef::Provider::Package::Yum::YumCache.instance end end describe "refresh" do it "should implicitly call yum-dump.py only once by default after being instantiated" do expect(@yc).to receive(:shell_out!).once @yc.installed_version("zlib") @yc.reset @yc.installed_version("zlib") end it "should run yum-dump.py using the system python when next_refresh is for :all" do @yc.reload expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --yum-lock-timeout 30$}, :timeout=>Chef::Config[:yum_timeout]) @yc.refresh end it "should run yum-dump.py with the installed flag when next_refresh is for :installed" do @yc.reload_installed expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --installed --yum-lock-timeout 30$}, :timeout=>Chef::Config[:yum_timeout]) @yc.refresh end it "should run yum-dump.py with the all-provides flag when next_refresh is for :provides" do @yc.reload_provides expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --all-provides --yum-lock-timeout 30$}, :timeout=>Chef::Config[:yum_timeout]) @yc.refresh end it "should pass extra_repo_control args to yum-dump.py" do @yc.enable_extra_repo_control("--enablerepo=foo --disablerepo=bar") expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --enablerepo=foo --disablerepo=bar --yum-lock-timeout 30$}, :timeout=>Chef::Config[:yum_timeout]) @yc.refresh end it "should pass extra_repo_control args and configured yum lock timeout to yum-dump.py" do Chef::Config[:yum_lock_timeout] = 999 @yc.enable_extra_repo_control("--enablerepo=foo --disablerepo=bar") expect(@yc).to receive(:shell_out!).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides --enablerepo=foo --disablerepo=bar --yum-lock-timeout 999$}, :timeout=>Chef::Config[:yum_timeout]) @yc.refresh end it "should warn about invalid data with too many separators" do @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_bad_separators, :stderr => @stderr) allow(@yc).to receive(:shell_out!).and_return(@status) expect(Chef::Log).to receive(:warn).exactly(3).times.with(%r{Problem parsing}) @yc.refresh end it "should warn about invalid data with an incorrect type" do @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_bad_type, :stderr => @stderr) allow(@yc).to receive(:shell_out!).and_return(@status) expect(Chef::Log).to receive(:warn).exactly(2).times.with(%r{Problem parsing}) @yc.refresh end it "should warn about no output from yum-dump.py" do @status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_no_output, :stderr => @stderr) allow(@yc).to receive(:shell_out!).and_return(@status) expect(Chef::Log).to receive(:warn).exactly(1).times.with(%r{no output from yum-dump.py}) @yc.refresh end it "should raise exception yum-dump.py exits with a non zero status" do @status = double("Status", :exitstatus => 1, :stdin => @stdin, :stdout => @stdout_no_output, :stderr => @stderr) allow(@yc).to receive(:shell_out!).and_return(@status) expect { @yc.refresh}.to raise_error(Chef::Exceptions::Package, %r{CentOS-Base.repo, line: 12}) end it "should parse type 'i' into an installed state for a package" do expect(@yc.available_version("erlang-mochiweb")).to eq(nil) expect(@yc.installed_version("erlang-mochiweb")).not_to eq(nil) end it "should parse type 'a' into an available state for a package" do expect(@yc.available_version("znc")).not_to eq(nil) expect(@yc.installed_version("znc")).to eq(nil) end it "should parse type 'r' into an installed and available states for a package" do expect(@yc.available_version("zip")).not_to eq(nil) expect(@yc.installed_version("zip")).not_to eq(nil) end it "should parse installonlypkgs from yum-dump.py options output" do expect(@yc.allow_multi_install).to eq(%w{kernel kernel-bigmem kernel-enterprise}) end end describe "installed_version" do it "should take one or two arguments" do expect { @yc.installed_version("zip") }.not_to raise_error expect { @yc.installed_version("zip", "i386") }.not_to raise_error expect { @yc.installed_version("zip", "i386", "extra") }.to raise_error(ArgumentError) end it "should return version-release for matching package regardless of arch" do expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5") expect(@yc.installed_version("zip", nil)).to eq("2.31-2.el5") end it "should return version-release for matching package and arch" do expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5") expect(@yc.installed_version("zisofs-tools", "i386")).to eq(nil) end it "should return nil for an unmatched package" do expect(@yc.installed_version(nil, nil)).to eq(nil) expect(@yc.installed_version("test1", nil)).to eq(nil) expect(@yc.installed_version("test2", "x86_64")).to eq(nil) end end describe "available_version" do it "should take one or two arguments" do expect { @yc.available_version("zisofs-tools") }.not_to raise_error expect { @yc.available_version("zisofs-tools", "i386") }.not_to raise_error expect { @yc.available_version("zisofs-tools", "i386", "extra") }.to raise_error(ArgumentError) end it "should return version-release for matching package regardless of arch" do expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5") expect(@yc.available_version("zip", nil)).to eq("2.31-2.el5") end it "should return version-release for matching package and arch" do expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5") expect(@yc.available_version("zisofs-tools", "i386")).to eq(nil) end it "should return nil for an unmatched package" do expect(@yc.available_version(nil, nil)).to eq(nil) expect(@yc.available_version("test1", nil)).to eq(nil) expect(@yc.available_version("test2", "x86_64")).to eq(nil) end end describe "version_available?" do it "should take two or three arguments" do expect { @yc.version_available?("zisofs-tools") }.to raise_error(ArgumentError) expect { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2") }.not_to raise_error expect { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64") }.not_to raise_error end it "should return true if our package-version-arch is available" do expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64")).to eq(true) end it "should return true if our package-version, no arch, is available" do expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", nil)).to eq(true) expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2")).to eq(true) end it "should return false if our package-version-arch isn't available" do expect(@yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "pretend")).to eq(false) expect(@yc.version_available?("zisofs-tools", "pretend", "x86_64")).to eq(false) expect(@yc.version_available?("pretend", "1.0.6-3.2.2", "x86_64")).to eq(false) end it "should return false if our package-version, no arch, isn't available" do expect(@yc.version_available?("zisofs-tools", "pretend", nil)).to eq(false) expect(@yc.version_available?("zisofs-tools", "pretend")).to eq(false) expect(@yc.version_available?("pretend", "1.0.6-3.2.2")).to eq(false) end end describe "package_repository" do it "should take two or three arguments" do expect { @yc.package_repository("zisofs-tools") }.to raise_error(ArgumentError) expect { @yc.package_repository("zisofs-tools", "1.0.6-3.2.2") }.not_to raise_error expect { @yc.package_repository("zisofs-tools", "1.0.6-3.2.2", "x86_64") }.not_to raise_error end it "should return repoid for package-version-arch" do expect(@yc.package_repository("zlib-devel", "1.2.3-3", "i386")).to eq("extras") expect(@yc.package_repository("zlib-devel", "1.2.3-3", "x86_64")).to eq("base") end it "should return repoid for package-version, no arch" do expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2", nil)).to eq("extras") expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2")).to eq("extras") end it "should return nil when no match for package-version-arch" do expect(@yc.package_repository("zisofs-tools", "1.0.6-3.2.2", "pretend")).to eq(nil) expect(@yc.package_repository("zisofs-tools", "pretend", "x86_64")).to eq(nil) expect(@yc.package_repository("pretend", "1.0.6-3.2.2", "x86_64")).to eq(nil) end it "should return nil when no match for package-version, no arch" do expect(@yc.package_repository("zisofs-tools", "pretend", nil)).to eq(nil) expect(@yc.package_repository("zisofs-tools", "pretend")).to eq(nil) expect(@yc.package_repository("pretend", "1.0.6-3.2.2")).to eq(nil) end end describe "reset" do it "should empty the installed and available packages RPMDb" do expect(@yc.available_version("zip", "x86_64")).to eq("2.31-2.el5") expect(@yc.installed_version("zip", "x86_64")).to eq("2.31-2.el5") @yc.reset expect(@yc.available_version("zip", "x86_64")).to eq(nil) expect(@yc.installed_version("zip", "x86_64")).to eq(nil) end end describe "package_available?" do it "should return true a package name is available" do expect(@yc.package_available?("zisofs-tools")).to eq(true) expect(@yc.package_available?("moo")).to eq(false) expect(@yc.package_available?(nil)).to eq(false) end it "should return true a package name + arch is available" do expect(@yc.package_available?("zlib-devel.i386")).to eq(true) expect(@yc.package_available?("zisofs-tools.x86_64")).to eq(true) expect(@yc.package_available?("znc-test.beta1.x86_64")).to eq(true) expect(@yc.package_available?("znc-test.beta1")).to eq(true) expect(@yc.package_available?("znc-test.test.beta1")).to eq(true) expect(@yc.package_available?("moo.i386")).to eq(false) expect(@yc.package_available?("zisofs-tools.beta")).to eq(false) expect(@yc.package_available?("znc-test.test")).to eq(false) end end describe "enable_extra_repo_control" do it "should set @extra_repo_control to arg" do @yc.enable_extra_repo_control("--enablerepo=test") expect(@yc.extra_repo_control).to eq("--enablerepo=test") end it "should call reload once when set to flag cache for update" do expect(@yc).to receive(:reload).once @yc.enable_extra_repo_control("--enablerepo=test") @yc.enable_extra_repo_control("--enablerepo=test") end end describe "disable_extra_repo_control" do it "should set @extra_repo_control to nil" do @yc.enable_extra_repo_control("--enablerepo=test") @yc.disable_extra_repo_control expect(@yc.extra_repo_control).to eq(nil) end it "should call reload once when cleared to flag cache for update" do expect(@yc).to receive(:reload).once @yc.enable_extra_repo_control("--enablerepo=test") expect(@yc).to receive(:reload).once @yc.disable_extra_repo_control @yc.disable_extra_repo_control end end end describe "Chef::Provider::Package::Yum - Multi" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new(['cups', 'vim']) @status = double("Status", :exitstatus => 0) @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, :reset => true, :installed_version => 'XXXX', :candidate_version => 'YYYY', :package_available? => true, :version_available? => true, :allow_multi_install => [ 'kernel' ], :package_repository => 'base', :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @pid = double("PID") end describe "when loading the current system state" do it "should create a current resource with the name of the new_resource" do @provider.load_current_resource expect(@provider.current_resource.name).to eq('cups, vim') end it "should set the current resources package name to the new resources package name" do @provider.load_current_resource expect(@provider.current_resource.package_name).to eq(['cups', 'vim']) end it "should set the installed version to nil on the current resource if no installed package" do allow(@yum_cache).to receive(:installed_version).and_return(nil) @provider.load_current_resource expect(@provider.current_resource.version).to eq([nil, nil]) end it "should set the installed version if yum has one" do allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5') allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('1.0') allow(@yum_cache).to receive(:candidate_version).with('cups', nil).and_return('1.2.4-11.18.el5_2.3') allow(@yum_cache).to receive(:candidate_version).with('vim', nil).and_return('1.5') @provider.load_current_resource expect(@provider.current_resource.version).to eq(['1.2.4-11.18.el5', '1.0']) end it "should set the candidate version if yum info has one" do allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5') allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('1.0') allow(@yum_cache).to receive(:candidate_version).with('cups', nil).and_return('1.2.4-11.18.el5_2.3') allow(@yum_cache).to receive(:candidate_version).with('vim', nil).and_return('1.5') @provider.load_current_resource expect(@provider.candidate_version).to eql(['1.2.4-11.18.el5_2.3', '1.5']) end it "should return the current resouce" do expect(@provider.load_current_resource).to eql(@provider.current_resource) end end describe "when installing a package" do it "should run yum install with the package name and version" do @provider.load_current_resource allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5') allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('0.9') expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.19.el5 vim-1.0" ) @provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0']) end it "should run yum install with the package name, version and arch" do @provider.load_current_resource allow(@new_resource).to receive(:arch).and_return("i386") allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y install cups-1.2.4-11.19.el5.i386 vim-1.0.i386" ) @provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", "1.0"]) end it "installs the package with the options given in the resource" do @provider.load_current_resource allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1) allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5') allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('0.9') expect(@provider).to receive(:yum_command).with( "yum -d0 -e0 -y --disablerepo epmd install cups-1.2.4-11.19.el5 vim-1.0" ) allow(@new_resource).to receive(:options).and_return("--disablerepo epmd") @provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0']) end end end chef-12.3.0/spec/unit/provider/package/freebsd/0000755000004100000410000000000012520074675021312 5ustar www-datawww-datachef-12.3.0/spec/unit/provider/package/freebsd/pkg_spec.rb0000644000004100000410000003310312520074675023432 0ustar www-datawww-data# # Authors:: Bryan McLellan (btm@loftninjas.org) # Matthew Landauer (matthew@openaustralia.org) # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer # 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 'ostruct' describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("zsh") @current_resource = Chef::Resource::Package.new("zsh") @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) @provider.current_resource = @current_resource allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(false) end describe "when determining the current package state" do before do allow(@provider).to receive(:ports_candidate_version).and_return("4.3.6") end it "should create a current resource with the name of the new_resource" do current_resource = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context).current_resource expect(current_resource.name).to eq("zsh") end it "should return a version if the package is installed" do expect(@provider).to receive(:current_installed_version).and_return("4.3.6_7") @provider.load_current_resource expect(@current_resource.version).to eq("4.3.6_7") end it "should return nil if the package is not installed" do expect(@provider).to receive(:current_installed_version).and_return(nil) @provider.load_current_resource expect(@current_resource.version).to be_nil end it "should return a candidate version if it exists" do expect(@provider).to receive(:current_installed_version).and_return(nil) @provider.load_current_resource expect(@provider.candidate_version).to eql("4.3.6") end end describe "when querying for package state and attributes" do before do #@new_resource = Chef::Resource::Package.new("zsh") #@provider = Chef::Provider::Package::Freebsd::Pkg.new(@node, @new_resource) #@status = double("Status", :exitstatus => 0) #@stdin = double("STDIN", :null_object => true) #@stdout = double("STDOUT", :null_object => true) #@stderr = double("STDERR", :null_object => true) #@pid = double("PID", :null_object => true) end it "should return the version number when it is installed" do pkg_info = OpenStruct.new(:stdout => "zsh-4.3.6_7") expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(pkg_info) #@provider.should_receive(:popen4).with('pkg_info -E "zsh*"').and_yield(@pid, @stdin, ["zsh-4.3.6_7"], @stderr).and_return(@status) allow(@provider).to receive(:package_name).and_return("zsh") expect(@provider.current_installed_version).to eq("4.3.6_7") end it "does not set the current version number when the package is not installed" do pkg_info = OpenStruct.new(:stdout => "") expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(pkg_info) allow(@provider).to receive(:package_name).and_return("zsh") expect(@provider.current_installed_version).to be_nil end it "should return the port path for a valid port name" do whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh") expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis) #@provider.should_receive(:popen4).with("whereis -s zsh").and_yield(@pid, @stdin, ["zsh: /usr/ports/shells/zsh"], @stderr).and_return(@status) allow(@provider).to receive(:port_name).and_return("zsh") expect(@provider.port_path).to eq("/usr/ports/shells/zsh") end # Not happy with the form of these tests as they are far too closely tied to the implementation and so very fragile. it "should return the ports candidate version when given a valid port path" do allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh") make_v = OpenStruct.new(:stdout => "4.3.6\n", :exitstatus => 0) expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", {:cwd=>"/usr/ports/shells/zsh", :returns=>[0, 1], :env=>nil}).and_return(make_v) expect(@provider.ports_candidate_version).to eq("4.3.6") end it "should figure out the package name when we have ports" do allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(true) allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh") make_v = OpenStruct.new(:stdout => "zsh-4.3.6_7\n", :exitstatus => 0) expect(@provider).to receive(:shell_out!).with("make -V PKGNAME", {:cwd=>"/usr/ports/shells/zsh", :env=>nil, :returns=>[0, 1]}).and_return(make_v) #@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7") expect(@provider.package_name).to eq("zsh") end end describe Chef::Provider::Package::Freebsd::Pkg, "install_package" do before(:each) do @cmd_result = OpenStruct.new(:status => true) @provider.current_resource = @current_resource allow(@provider).to receive(:package_name).and_return("zsh") allow(@provider).to receive(:latest_link_name).and_return("zsh") allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh") end it "should run pkg_add -r with the package name" do expect(@provider).to receive(:shell_out!).with("pkg_add -r zsh", :env => nil).and_return(@cmd_result) @provider.install_package("zsh", "4.3.6_7") end end describe Chef::Provider::Package::Freebsd::Pkg, "port path" do before do #@node = Chef::Node.new @new_resource = Chef::Resource::Package.new("zsh") @new_resource.cookbook_name = "adventureclub" @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) end it "should figure out the port path from the package_name using whereis" do whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh") expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env=>nil).and_return(whereis) expect(@provider.port_path).to eq("/usr/ports/shells/zsh") end it "should use the package_name as the port path when it starts with /" do new_resource = Chef::Resource::Package.new("/usr/ports/www/wordpress") provider = Chef::Provider::Package::Freebsd::Pkg.new(new_resource, @run_context) expect(provider).not_to receive(:popen4) expect(provider.port_path).to eq("/usr/ports/www/wordpress") end it "should use the package_name as a relative path from /usr/ports when it contains / but doesn't start with it" do # @new_resource = double( "Chef::Resource::Package", # :package_name => "www/wordpress", # :cookbook_name => "xenoparadox") new_resource = Chef::Resource::Package.new("www/wordpress") provider = Chef::Provider::Package::Freebsd::Pkg.new(new_resource, @run_context) expect(provider).not_to receive(:popen4) expect(provider.port_path).to eq("/usr/ports/www/wordpress") end end describe Chef::Provider::Package::Freebsd::Pkg, "ruby-iconv (package with a dash in the name)" do before(:each) do @new_resource = Chef::Resource::Package.new("ruby-iconv") @current_resource = Chef::Resource::Package.new("ruby-iconv") @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) @provider.current_resource = @current_resource allow(@provider).to receive(:port_path).and_return("/usr/ports/converters/ruby-iconv") allow(@provider).to receive(:package_name).and_return("ruby18-iconv") allow(@provider).to receive(:latest_link_name).and_return("ruby18-iconv") @install_result = OpenStruct.new(:status => true) end it "should run pkg_add -r with the package name" do expect(@provider).to receive(:shell_out!).with("pkg_add -r ruby18-iconv", :env => nil).and_return(@install_result) @provider.install_package("ruby-iconv", "1.0") end end describe Chef::Provider::Package::Freebsd::Pkg, "remove_package" do before(:each) do @pkg_delete = OpenStruct.new(:status => true) @new_resource.version "4.3.6_7" @current_resource.version "4.3.6_7" @provider.current_resource = @current_resource allow(@provider).to receive(:package_name).and_return("zsh") end it "should run pkg_delete with the package name and version" do expect(@provider).to receive(:shell_out!).with("pkg_delete zsh-4.3.6_7", :env => nil).and_return(@pkg_delete) @provider.remove_package("zsh", "4.3.6_7") end end # CHEF-4371 # There are some port names that contain special characters such as +'s. This breaks the regular expression used to determine what # version of a package is currently installed and to get the port_path. # Example package name: bonnie++ describe Chef::Provider::Package::Freebsd::Pkg, "bonnie++ (package with a plus in the name :: CHEF-4371)" do before(:each) do @new_resource = Chef::Resource::Package.new("bonnie++") @current_resource = Chef::Resource::Package.new("bonnie++") @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) @provider.current_resource = @current_resource end it "should return the port path for a valid port name" do whereis = OpenStruct.new(:stdout => "bonnie++: /usr/ports/benchmarks/bonnie++") expect(@provider).to receive(:shell_out!).with("whereis -s bonnie++", :env => nil).and_return(whereis) allow(@provider).to receive(:port_name).and_return("bonnie++") expect(@provider.port_path).to eq("/usr/ports/benchmarks/bonnie++") end it "should return the version number when it is installed" do pkg_info = OpenStruct.new(:stdout => "bonnie++-1.96") expect(@provider).to receive(:shell_out!).with('pkg_info -E "bonnie++*"', :env => nil, :returns => [0,1]).and_return(pkg_info) allow(@provider).to receive(:package_name).and_return("bonnie++") expect(@provider.current_installed_version).to eq("1.96") end end # A couple of examples to show up the difficulty of determining the command to install the binary package given the port: # PORT DIRECTORY INSTALLED PACKAGE NAME COMMAND TO INSTALL PACKAGE # /usr/ports/lang/perl5.8 perl-5.8.8_1 pkg_add -r perl # /usr/ports/databases/mysql50-server mysql-server-5.0.45_1 pkg_add -r mysql50-server # # So, in one case it appears the command to install the package can be derived from the name of the port directory and in the # other case it appears the command can be derived from the package name. Very confusing! # Well, luckily, after much poking around, I discovered that the two can be disambiguated through the use of the LATEST_LINK # variable which is set by the ports Makefile # # PORT DIRECTORY LATEST_LINK INSTALLED PACKAGE NAME COMMAND TO INSTALL PACKAGE # /usr/ports/lang/perl5.8 perl perl-5.8.8_1 pkg_add -r perl # /usr/ports/databases/mysql50-server mysql50-server mysql-server-5.0.45_1 pkg_add -r mysql50-server # # The variable LATEST_LINK is named that way because the directory that "pkg_add -r" downloads from is called "Latest" and # contains the "latest" versions of package as symbolic links to the files in the "All" directory. describe Chef::Provider::Package::Freebsd::Pkg, "install_package latest link fixes" do it "should install the perl binary package with the correct name" do @new_resource = Chef::Resource::Package.new("perl5.8") @current_resource = Chef::Resource::Package.new("perl5.8") @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) @provider.current_resource = @current_resource allow(@provider).to receive(:package_name).and_return("perl") allow(@provider).to receive(:latest_link_name).and_return("perl") cmd = OpenStruct.new(:status => true) expect(@provider).to receive(:shell_out!).with("pkg_add -r perl", :env => nil).and_return(cmd) @provider.install_package("perl5.8", "5.8.8_1") end it "should install the mysql50-server binary package with the correct name" do @new_resource = Chef::Resource::Package.new("mysql50-server") @current_resource = Chef::Resource::Package.new("mysql50-server") @provider = Chef::Provider::Package::Freebsd::Pkg.new(@new_resource, @run_context) @provider.current_resource = @current_resource allow(@provider).to receive(:package_name).and_return("mysql-server") allow(@provider).to receive(:latest_link_name).and_return("mysql50-server") cmd = OpenStruct.new(:status => true) expect(@provider).to receive(:shell_out!).with("pkg_add -r mysql50-server", :env=>nil).and_return(cmd) @provider.install_package("mysql50-server", "5.0.45_1") end end end chef-12.3.0/spec/unit/provider/package/freebsd/pkgng_spec.rb0000644000004100000410000001327512520074675023767 0ustar www-datawww-data# # Authors:: Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2014 Richard Manyanza # 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 'ostruct' describe Chef::Provider::Package::Freebsd::Port do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("zsh") @provider = Chef::Provider::Package::Freebsd::Pkgng.new(@new_resource, @run_context) end describe "initialization" do it "should create a current resource with the name of the new resource" do expect(@provider.current_resource.is_a?(Chef::Resource::Package)).to be_truthy expect(@provider.current_resource.name).to eq('zsh') end end describe "loading current resource" do before(:each) do allow(@provider).to receive(:current_installed_version) allow(@provider).to receive(:candidate_version) end it "should set the package name" do @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("zsh") end it "should set the current version" do expect(@provider).to receive(:current_installed_version).and_return("5.0.2") @provider.load_current_resource expect(@provider.current_resource.version).to eq("5.0.2") end it "should set the candidate version" do expect(@provider).to receive(:candidate_version).and_return("5.0.5") @provider.load_current_resource expect(@provider.instance_variable_get(:"@candidate_version")).to eq("5.0.5") end end describe "determining current installed version" do before(:each) do allow(@provider).to receive(:supports_pkgng?) @pkg_info = OpenStruct.new(:stdout => "zsh-3.1.7\nVersion : 3.1.7\n") end it "should query pkg database" do expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info) expect(@provider.current_installed_version).to eq("3.1.7") end end describe "determining candidate version" do it "should query repository" do pkg_query = OpenStruct.new(:stdout => "5.0.5\n", :exitstatus => 0) expect(@provider).to receive(:shell_out!).with("pkg rquery '%v' zsh", :env => nil).and_return(pkg_query) expect(@provider.candidate_version).to eq("5.0.5") end it "should query specified repository when given option" do @provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration. pkg_query = OpenStruct.new(:stdout => "5.0.3\n", :exitstatus => 0) expect(@provider).to receive(:shell_out!).with("pkg rquery -r LocalMirror '%v' zsh", :env => nil).and_return(pkg_query) expect(@provider.candidate_version).to eq("5.0.3") end it "should return candidate version from file when given a file" do @provider.new_resource.source("/nas/pkg/repo/zsh-5.0.1.txz") expect(@provider.candidate_version).to eq("5.0.1") end end describe "installing a binary package" do before(:each) do @install_result = OpenStruct.new(:status => true) end it "should handle package source from file" do @provider.new_resource.source("/nas/pkg/repo/zsh-5.0.1.txz") expect(@provider).to receive(:shell_out!). with("pkg add /nas/pkg/repo/zsh-5.0.1.txz", :env => { 'LC_ALL' => nil }). and_return(@install_result) @provider.install_package("zsh", "5.0.1") end it "should handle package source over ftp or http" do @provider.new_resource.source("http://repo.example.com/zsh-5.0.1.txz") expect(@provider).to receive(:shell_out!). with("pkg add http://repo.example.com/zsh-5.0.1.txz", :env => { 'LC_ALL' => nil }). and_return(@install_result) @provider.install_package("zsh", "5.0.1") end it "should handle a package name" do expect(@provider).to receive(:shell_out!). with("pkg install -y zsh", :env => { 'LC_ALL' => nil }).and_return(@install_result) @provider.install_package("zsh", "5.0.1") end it "should handle a package name with a specified repo" do @provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration. expect(@provider).to receive(:shell_out!). with("pkg install -y -r LocalMirror zsh", :env => { 'LC_ALL' => nil }).and_return(@install_result) @provider.install_package("zsh", "5.0.1") end end describe "removing a binary package" do before(:each) do @install_result = OpenStruct.new(:status => true) end it "should call pkg delete" do expect(@provider).to receive(:shell_out!). with("pkg delete -y zsh-5.0.1", :env => nil).and_return(@install_result) @provider.remove_package("zsh", "5.0.1") end it "should not include repo option in pkg delete" do @provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration. expect(@provider).to receive(:shell_out!). with("pkg delete -y zsh-5.0.1", :env => nil).and_return(@install_result) @provider.remove_package("zsh", "5.0.1") end end end chef-12.3.0/spec/unit/provider/package/freebsd/port_spec.rb0000644000004100000410000001534312520074675023643 0ustar www-datawww-data# # Authors:: Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2014 Richard Manyanza # 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 'ostruct' describe Chef::Provider::Package::Freebsd::Port do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::FreebsdPackage.new("zsh", @run_context) @provider = Chef::Provider::Package::Freebsd::Port.new(@new_resource, @run_context) end describe "initialization" do it "should create a current resource with the name of the new resource" do expect(@provider.current_resource.is_a?(Chef::Resource::Package)).to be_truthy expect(@provider.current_resource.name).to eq('zsh') end end describe "loading current resource" do before(:each) do allow(@provider).to receive(:current_installed_version) allow(@provider).to receive(:candidate_version) end it "should set the package name" do @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("zsh") end it "should set the current version" do expect(@provider).to receive(:current_installed_version).and_return("5.0.2") @provider.load_current_resource expect(@provider.current_resource.version).to eq("5.0.2") end it "should set the candidate version" do expect(@provider).to receive(:candidate_version).and_return("5.0.5") @provider.load_current_resource expect(@provider.instance_variable_get(:"@candidate_version")).to eq("5.0.5") end end describe "determining current installed version" do before(:each) do @pkg_info = OpenStruct.new(:stdout => "zsh-3.1.7\n") end it "should check 'pkg_info' if system uses pkg_* tools" do allow(@new_resource).to receive(:supports_pkgng?) expect(@new_resource).to receive(:supports_pkgng?).and_return(false) expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(@pkg_info) expect(@provider.current_installed_version).to eq("3.1.7") end it "should check 'pkg info' if make supports WITH_PKGNG if freebsd version is < 1000017" do pkg_enabled = OpenStruct.new(:stdout => "yes\n") [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version| @node.automatic_attrs[:os_version] = __freebsd_version expect(@new_resource).to receive(:shell_out!).with('make -V WITH_PKGNG', :env => nil).and_return(pkg_enabled) expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info) expect(@provider.current_installed_version).to eq("3.1.7") end end it "should check 'pkg info' if the freebsd version is greater than or equal to 1000017" do __freebsd_version = 1000017 @node.automatic_attrs[:os_version] = __freebsd_version expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info) expect(@provider.current_installed_version).to eq("3.1.7") end end describe "determining candidate version" do before(:each) do @port_version = OpenStruct.new(:stdout => "5.0.5\n", :exitstatus => 0) end it "should return candidate version if port exists" do allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(true) allow(@provider).to receive(:port_dir).and_return('/usr/ports/shells/zsh') expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", :cwd => "/usr/ports/shells/zsh", :env => nil, :returns => [0,1]). and_return(@port_version) expect(@provider.candidate_version).to eq("5.0.5") end it "should raise exception if ports tree not found" do allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(false) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package, "Ports collection could not be found") end end describe "determining port directory" do it "should return name if package name is absolute path" do allow(@provider.new_resource).to receive(:package_name).and_return("/var/ports/shells/zsh") expect(@provider.port_dir).to eq("/var/ports/shells/zsh") end it "should return full ports path given package name and category" do allow(@provider.new_resource).to receive(:package_name).and_return("shells/zsh") expect(@provider.port_dir).to eq("/usr/ports/shells/zsh") end it "should query system for path given just a name" do whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh\n") expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis) expect(@provider.port_dir).to eq("/usr/ports/shells/zsh") end it "should raise exception if not found" do whereis = OpenStruct.new(:stdout => "zsh:\n") expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis) expect { @provider.port_dir }.to raise_error(Chef::Exceptions::Package, "Could not find port with the name zsh") end end describe "building a binary package" do before(:each) do @install_result = OpenStruct.new(:status => true) end it "should run make install in port directory" do allow(@provider).to receive(:port_dir).and_return("/usr/ports/shells/zsh") expect(@provider).to receive(:shell_out!). with("make -DBATCH install clean", :timeout => 1800, :cwd => "/usr/ports/shells/zsh", :env => nil). and_return(@install_result) @provider.install_package("zsh", "5.0.5") end end describe "removing a binary package" do before(:each) do @install_result = OpenStruct.new(:status => true) end it "should run make deinstall in port directory" do allow(@provider).to receive(:port_dir).and_return("/usr/ports/shells/zsh") expect(@provider).to receive(:shell_out!). with("make deinstall", :timeout => 300, :cwd => "/usr/ports/shells/zsh", :env => nil). and_return(@install_result) @provider.remove_package("zsh", "5.0.5") end end end chef-12.3.0/spec/unit/provider/package/openbsd_spec.rb0000644000004100000410000001436012520074675022675 0ustar www-datawww-data# # Author:: Scott Bonds (scott@ggr.com) # Copyright:: Copyright (c) 2014 Scott Bonds # 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 'ostruct' describe Chef::Provider::Package::Openbsd do let(:node) do node = Chef::Node.new node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'} node end let (:provider) do events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) Chef::Provider::Package::Openbsd.new(new_resource, run_context) end let(:new_resource) { Chef::Resource::Package.new(name)} before(:each) do ENV['PKG_PATH'] = nil end describe "install a package" do let(:name) { 'ihavetoes' } let(:version) {'0.0'} context 'when not already installed' do before do allow(provider).to receive(:shell_out!).with("pkg_info -e \"#{name}->0\"", anything()).and_return(instance_double('shellout', :stdout => '')) end context 'when there is a single candidate' do context 'when installing from source' do it 'should run the installation command' do pending('Installing from source is not supported yet') # This is a consequence of load_current_resource being called before define_resource_requirements # It can be deleted once an implementation is provided allow(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( instance_double('shellout', :stdout => "#{name}-#{version}\n")) new_resource.source('/some/path/on/disk.tgz') provider.run_action(:install) end end context 'when source is not provided' do it 'should run the installation command' do expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( instance_double('shellout', :stdout => "#{name}-#{version}\n")) expect(provider).to receive(:shell_out!).with( "pkg_add -r #{name}-#{version}", {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} ) {OpenStruct.new :status => true} provider.run_action(:install) end end end context 'when there are multiple candidates' do let(:flavor_a) { 'flavora' } let(:flavor_b) { 'flavorb' } context 'if no version is specified' do it 'should raise an exception' do expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n")) expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /multiple matching candidates/) end end context 'if a flavor is specified' do let(:flavor) { 'flavora' } let(:package_name) {'ihavetoes' } let(:name) { "#{package_name}--#{flavor}" } context 'if no version is specified' do it 'should run the installation command' do expect(provider).to receive(:shell_out!).with("pkg_info -e \"#{package_name}->0\"", anything()).and_return(instance_double('shellout', :stdout => '')) expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return( instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor}\n")) expect(provider).to receive(:shell_out!).with( "pkg_add -r #{name}-#{version}-#{flavor}", {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} ) {OpenStruct.new :status => true} provider.run_action(:install) end end context 'if a version is specified' do it 'runs the installation command' do pending('Specifying both a version and flavor is not supported') new_resource.version(version) allow(provider).to receive(:shell_out!).with(/pkg_info -e/, anything()).and_return(instance_double('shellout', :stdout => '')) allow(provider).to receive(:candidate_version).and_return("#{package_name}-#{version}-#{flavor}") provider.run_action(:install) end end end context 'if a version is specified' do it 'should use the flavor from the version' do expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}-#{version}-#{flavor_b}\"", anything()).and_return( instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n")) new_resource.version("#{version}-#{flavor_b}") expect(provider).to receive(:shell_out!).with( "pkg_add -r #{name}-#{version}-#{flavor_b}", {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}} ) {OpenStruct.new :status => true} provider.run_action(:install) end end end end end describe "delete a package" do before do @name = 'ihavetoes' @new_resource = Chef::Resource::Package.new(@name) @current_resource = Chef::Resource::Package.new(@name) @provider = Chef::Provider::Package::Openbsd.new(@new_resource, @run_context) @provider.current_resource = @current_resource end it "should run the command to delete the installed package" do expect(@provider).to receive(:shell_out!).with( "pkg_delete #{@name}", :env=>nil ) {OpenStruct.new :status => true} @provider.remove_package(@name, nil) end end end chef-12.3.0/spec/unit/provider/package/easy_install_spec.rb0000644000004100000410000001016612520074675023732 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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::Package::EasyInstall do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::EasyInstallPackage.new('boto') @new_resource.version('1.8d') @current_resource = Chef::Resource::EasyInstallPackage.new('boto') @current_resource.version('1.8d') @provider = Chef::Provider::Package::EasyInstall.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @stdin = StringIO.new @stdout = StringIO.new @status = double("Status", :exitstatus => 0) @stderr = StringIO.new @pid = 2342 allow(@provider).to receive(:popen4).and_return(@status) end describe "easy_install_binary_path" do it "should return a Chef::Provider::EasyInstall object" do provider = Chef::Provider::Package::EasyInstall.new(@node, @new_resource) expect(provider).to be_a_kind_of(Chef::Provider::Package::EasyInstall) end it "should set the current resources package name to the new resources package name" do allow($stdout).to receive(:write) expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should return a relative path to easy_install if no easy_install_binary is given" do expect(@provider.easy_install_binary_path).to eql("easy_install") end it "should return a specific path to easy_install if a easy_install_binary is given" do expect(@new_resource).to receive(:easy_install_binary).and_return("/opt/local/bin/custom/easy_install") expect(@provider.easy_install_binary_path).to eql("/opt/local/bin/custom/easy_install") end end describe "actions_on_package" do it "should run easy_install with the package name and version" do expect(@provider).to receive(:run_command).with({ :command => "easy_install \"boto==1.8d\"" }) @provider.install_package("boto", "1.8d") end it "should run easy_install with the package name and version and specified options" do expect(@provider).to receive(:run_command).with({ :command => "easy_install --always-unzip \"boto==1.8d\"" }) allow(@new_resource).to receive(:options).and_return("--always-unzip") @provider.install_package("boto", "1.8d") end it "should run easy_install with the package name and version" do expect(@provider).to receive(:run_command).with({ :command => "easy_install \"boto==1.8d\"" }) @provider.upgrade_package("boto", "1.8d") end it "should run easy_install -m with the package name and version" do expect(@provider).to receive(:run_command).with({ :command => "easy_install -m boto" }) @provider.remove_package("boto", "1.8d") end it "should run easy_install -m with the package name and version and specified options" do expect(@provider).to receive(:run_command).with({ :command => "easy_install -x -m boto" }) allow(@new_resource).to receive(:options).and_return("-x") @provider.remove_package("boto", "1.8d") end it "should run easy_install -m with the package name and version" do expect(@provider).to receive(:run_command).with({ :command => "easy_install -m boto" }) @provider.purge_package("boto", "1.8d") end end end chef-12.3.0/spec/unit/provider/package/pacman_spec.rb0000644000004100000410000001605712520074675022507 0ustar www-datawww-data# # Author:: Jan Zimmek () # Copyright:: Copyright (c) 2010 Jan Zimmek # 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::Package::Pacman do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("nano") @current_resource = Chef::Resource::Package.new("nano") @status = double(:stdout => "", :exitstatus => 0) @provider = Chef::Provider::Package::Pacman.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) allow(@provider).to receive(:shell_out).and_return(@status) @stdin = StringIO.new @stdout = StringIO.new(<<-ERR) error: package "nano" not found ERR @stderr = StringIO.new @pid = 2342 end describe "when determining the current package state" do it "should create a current resource with the name of the new_resource" do expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources package name to the new resources package name" do expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should run pacman query with the package name" do expect(@provider).to receive(:shell_out).with("pacman -Qi #{@new_resource.package_name}").and_return(@status) @provider.load_current_resource end it "should read stdout on pacman" do allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource end it "should set the installed version to nil on the current resource if pacman installed version not exists" do allow(@provider).to receive(:shell_out).and_return(@status) expect(@current_resource).to receive(:version).with(nil).and_return(true) @provider.load_current_resource end it "should set the installed version if pacman has one" do stdout = <<-PACMAN Name : nano Version : 2.2.2-1 URL : http://www.nano-editor.org Licenses : GPL Groups : base Provides : None Depends On : glibc ncurses Optional Deps : None Required By : None Conflicts With : None Replaces : None Installed Size : 1496.00 K Packager : Andreas Radke Architecture : i686 Build Date : Mon 18 Jan 2010 06:16:16 PM CET Install Date : Mon 01 Feb 2010 10:06:30 PM CET Install Reason : Explicitly installed Install Script : Yes Description : Pico editor clone with enhancements PACMAN status = double(:stdout => stdout, :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(status) @provider.load_current_resource expect(@current_resource.version).to eq("2.2.2-1") end it "should set the candidate version if pacman has one" do status = double(:stdout => "core nano 2.2.3-1", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(status) @provider.load_current_resource expect(@provider.candidate_version).to eql("2.2.3-1") end it "should use pacman.conf to determine valid repo names for package versions" do @pacman_conf = <<-PACMAN_CONF [options] HoldPkg = pacman glibc Architecture = auto [customrepo] Server = https://my.custom.repo [core] Include = /etc/pacman.d/mirrorlist [extra] Include = /etc/pacman.d/mirrorlist [community] Include = /etc/pacman.d/mirrorlist PACMAN_CONF status = double(:stdout => "customrepo nano 1.2.3-4", :exitstatus => 0) allow(::File).to receive(:exists?).with("/etc/pacman.conf").and_return(true) allow(::File).to receive(:read).with("/etc/pacman.conf").and_return(@pacman_conf) allow(@provider).to receive(:shell_out).and_return(status) @provider.load_current_resource expect(@provider.candidate_version).to eql("1.2.3-4") end it "should raise an exception if pacman fails" do expect(@status).to receive(:exitstatus).and_return(2) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should not raise an exception if pacman succeeds" do expect(@status).to receive(:exitstatus).and_return(0) expect { @provider.load_current_resource }.not_to raise_error end it "should raise an exception if pacman does not return a candidate version" do allow(@provider).to receive(:shell_out).and_return(@status) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package) end it "should return the current resouce" do expect(@provider.load_current_resource).to eql(@current_resource) end end describe Chef::Provider::Package::Pacman, "install_package" do it "should run pacman install with the package name and version" do expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar nano") @provider.install_package("nano", "1.0") end it "should run pacman install with the package name and version and options if specified" do expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar --debug nano") allow(@new_resource).to receive(:options).and_return("--debug") @provider.install_package("nano", "1.0") end end describe Chef::Provider::Package::Pacman, "upgrade_package" do it "should run install_package with the name and version" do expect(@provider).to receive(:install_package).with("nano", "1.0") @provider.upgrade_package("nano", "1.0") end end describe Chef::Provider::Package::Pacman, "remove_package" do it "should run pacman remove with the package name" do expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar nano") @provider.remove_package("nano", "1.0") end it "should run pacman remove with the package name and options if specified" do expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar --debug nano") allow(@new_resource).to receive(:options).and_return("--debug") @provider.remove_package("nano", "1.0") end end describe Chef::Provider::Package::Pacman, "purge_package" do it "should run remove_package with the name and version" do expect(@provider).to receive(:remove_package).with("nano", "1.0") @provider.purge_package("nano", "1.0") end end end chef-12.3.0/spec/unit/provider/package/rubygems_spec.rb0000644000004100000410000010143712520074675023102 0ustar www-datawww-data# # Author:: David Balatero (dbalatero@gmail.com) # # Copyright:: Copyright (c) 2009 David Balatero # 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 'pp' module GemspecBackcompatCreator def gemspec(name, version) if Gem::Specification.new.method(:initialize).arity == 0 Gem::Specification.new { |s| s.name = name; s.version = version } else Gem::Specification.new(name, version) end end end require 'spec_helper' require 'ostruct' describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do include GemspecBackcompatCreator before do @gem_env = Chef::Provider::Package::Rubygems::CurrentGemEnvironment.new end it "determines the gem paths from the in memory rubygems" do expect(@gem_env.gem_paths).to eq(Gem.path) end it "determines the installed versions of gems from Gem.source_index" do gems = [gemspec('rspec-core', Gem::Version.new('1.2.9')), gemspec('rspec-core', Gem::Version.new('1.3.0'))] if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0') expect(Gem::Specification).to receive(:find_all_by_name).with('rspec-core', Gem::Dependency.new('rspec-core').requirement).and_return(gems) else expect(Gem.source_index).to receive(:search).with(Gem::Dependency.new('rspec-core', nil)).and_return(gems) end expect(@gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil))).to eq(gems) end it "determines the installed versions of gems from the source index (part2: the unmockening)" do expected = ['rspec-core', Gem::Version.new(RSpec::Core::Version::STRING)] actual = @gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).map { |spec| [spec.name, spec.version] } expect(actual).to include(expected) end it "yields to a block with an alternate source list set" do sources_in_block = nil normal_sources = Gem.sources begin @gem_env.with_gem_sources("http://gems.example.org") do sources_in_block = Gem.sources raise RuntimeError, "sources should be reset even in case of an error" end rescue RuntimeError end expect(sources_in_block).to eq(%w{http://gems.example.org}) expect(Gem.sources).to eq(normal_sources) end it "it doesnt alter the gem sources if none are set" do sources_in_block = nil normal_sources = Gem.sources begin @gem_env.with_gem_sources(nil) do sources_in_block = Gem.sources raise RuntimeError, "sources should be reset even in case of an error" end rescue RuntimeError end expect(sources_in_block).to eq(normal_sources) expect(Gem.sources).to eq(normal_sources) end it "finds a matching gem candidate version" do dep = Gem::Dependency.new('rspec', '>= 0') dep_installer = Gem::DependencyInstaller.new allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer) latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]] expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest) expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0'))).to eq(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 allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer) best_gem = double("best gem match", :spec => gemspec("rspec", Gem::Version.new("1.3.0")), :source => "https://rubygems.org") available_set = double("Gem::AvailableSet test double") expect(available_set).to receive(:pick_best!) expect(available_set).to receive(:set).and_return([best_gem]) expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(available_set) expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0'))).to eq(Gem::Version.new('1.3.0')) end context "when rubygems was upgraded from 1.8->2.0" do # https://github.com/rubygems/rubygems/issues/404 # tl;dr rubygems 1.8 and 2.0 can both be in the load path, which means that # require "rubygems/format" will load even though rubygems 2.0 doesn't have # that file. before do if defined?(Gem::Format) # tests are running under rubygems 1.8, or 2.0 upgraded from 1.8 @remove_gem_format = false else Gem.const_set(:Format, Object.new) @remove_gem_format = true end allow(Gem::Package).to receive(:respond_to?).and_call_original allow(Gem::Package).to receive(:respond_to?).with(:open).and_return(false) end after do if @remove_gem_format Gem.send(:remove_const, :Format) end end it "finds a matching gem candidate version on rubygems 2.0+ with some rubygems 1.8 code loaded" do package = double("Gem::Package", :spec => "a gemspec from package") expect(Gem::Package).to receive(:new).with("/path/to/package.gem").and_return(package) expect(@gem_env.spec_from_file("/path/to/package.gem")).to eq("a gemspec from package") end end it "gives the candidate version as nil if none is found" do dep = Gem::Dependency.new('rspec', '>= 0') latest = [] dep_installer = Gem::DependencyInstaller.new allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer) expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest) expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0'))).to be_nil end it "finds a matching candidate version from a .gem file when the path to the gem is supplied" do location = CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem' expect(@gem_env.candidate_version_from_file(Gem::Dependency.new('chef-integration-test', '>= 0'), location)).to eq(Gem::Version.new('0.1.0')) expect(@gem_env.candidate_version_from_file(Gem::Dependency.new('chef-integration-test', '>= 0.2.0'), location)).to be_nil end it "finds a matching gem from a specific gemserver when explicit sources are given" do dep = Gem::Dependency.new('rspec', '>= 0') latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]] expect(@gem_env).to receive(:with_gem_sources).with('http://gems.example.com').and_yield dep_installer = Gem::DependencyInstaller.new allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer) expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest) expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>=0'), 'http://gems.example.com')).to eq(Gem::Version.new('1.3.0')) end it "installs a gem with a hash of options for the dependency installer" do dep_installer = Gem::DependencyInstaller.new expect(@gem_env).to receive(:dependency_installer).with(:install_dir => '/foo/bar').and_return(dep_installer) expect(@gem_env).to receive(:with_gem_sources).with('http://gems.example.com').and_yield expect(dep_installer).to receive(:install).with(Gem::Dependency.new('rspec', '>= 0')) @gem_env.install(Gem::Dependency.new('rspec', '>= 0'), :install_dir => '/foo/bar', :sources => ['http://gems.example.com']) end it "builds an uninstaller for a gem with options set to avoid requiring user input" do # default options for uninstaller should be: # :ignore => true, :executables => true expect(Gem::Uninstaller).to receive(:new).with('rspec', :ignore => true, :executables => true) @gem_env.uninstaller('rspec') end it "uninstalls all versions of a gem" do uninstaller = double('gem uninstaller') expect(uninstaller).to receive(:uninstall) expect(@gem_env).to receive(:uninstaller).with('rspec', :all => true).and_return(uninstaller) @gem_env.uninstall('rspec') end it "uninstalls a specific version of a gem" do uninstaller = double('gem uninstaller') expect(uninstaller).to receive(:uninstall) expect(@gem_env).to receive(:uninstaller).with('rspec', :version => '1.2.3').and_return(uninstaller) @gem_env.uninstall('rspec', '1.2.3') end end describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do include GemspecBackcompatCreator before do Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache.clear Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache.clear @gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new('/usr/weird/bin/gem') end it "determines the gem paths from shelling out to gem env" do gem_env_output = ['/path/to/gems', '/another/path/to/gems'].join(File::PATH_SEPARATOR) shell_out_result = OpenStruct.new(:stdout => gem_env_output) expect(@gem_env).to receive(:shell_out!).with('/usr/weird/bin/gem env gempath').and_return(shell_out_result) expect(@gem_env.gem_paths).to eq(['/path/to/gems', '/another/path/to/gems']) end it "caches the gempaths by gem_binary" do gem_env_output = ['/path/to/gems', '/another/path/to/gems'].join(File::PATH_SEPARATOR) shell_out_result = OpenStruct.new(:stdout => gem_env_output) expect(@gem_env).to receive(:shell_out!).with('/usr/weird/bin/gem env gempath').and_return(shell_out_result) expected = ['/path/to/gems', '/another/path/to/gems'] expect(@gem_env.gem_paths).to eq(['/path/to/gems', '/another/path/to/gems']) expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache['/usr/weird/bin/gem']).to eq(expected) end it "uses the cached result for gem paths when available" do gem_env_output = ['/path/to/gems', '/another/path/to/gems'].join(File::PATH_SEPARATOR) shell_out_result = OpenStruct.new(:stdout => gem_env_output) expect(@gem_env).not_to receive(:shell_out!) expected = ['/path/to/gems', '/another/path/to/gems'] Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache['/usr/weird/bin/gem']= expected expect(@gem_env.gem_paths).to eq(['/path/to/gems', '/another/path/to/gems']) end it "builds the gems source index from the gem paths" do allow(@gem_env).to receive(:gem_paths).and_return(['/path/to/gems', '/another/path/to/gems']) if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0') @gem_env.gem_specification expect(Gem::Specification.dirs).to eq([ '/path/to/gems/specifications', '/another/path/to/gems/specifications' ]) else expect(Gem::SourceIndex).to receive(:from_gems_in).with('/path/to/gems/specifications', '/another/path/to/gems/specifications') @gem_env.gem_source_index end end it "determines the installed versions of gems from the source index" do gems = [gemspec('rspec', Gem::Version.new('1.2.9')), gemspec('rspec', Gem::Version.new('1.3.0'))] rspec_dep = Gem::Dependency.new('rspec', nil) if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0') allow(@gem_env).to receive(:gem_specification).and_return(Gem::Specification) expect(@gem_env.gem_specification).to receive(:find_all_by_name).with(rspec_dep.name, rspec_dep.requirement).and_return(gems) else allow(@gem_env).to receive(:gem_source_index).and_return(Gem.source_index) expect(@gem_env.gem_source_index).to receive(:search).with(rspec_dep).and_return(gems) end expect(@gem_env.installed_versions(Gem::Dependency.new('rspec', nil))).to eq(gems) end it "determines the installed versions of gems from the source index (part2: the unmockening)" do allow($stdout).to receive(:write) path_to_gem = if windows? `where gem`.split[1] else `which gem`.strip end pending("cant find your gem executable") if path_to_gem.empty? gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem) expected = ['rspec-core', Gem::Version.new(RSpec::Core::Version::STRING)] actual = gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).map { |s| [s.name, s.version] } expect(actual).to include(expected) end it "detects when the target gem environment is the jruby platform" do gem_env_out=<<-JRUBY_GEM_ENV RubyGems Environment: - RUBYGEMS VERSION: 1.3.6 - RUBY VERSION: 1.8.7 (2010-05-12 patchlevel 249) [java] - INSTALLATION DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0 - RUBY EXECUTABLE: /Users/you/.rvm/rubies/jruby-1.5.0/bin/jruby - EXECUTABLE DIRECTORY: /Users/you/.rvm/gems/jruby-1.5.0/bin - RUBYGEMS PLATFORMS: - ruby - universal-java-1.6 - GEM PATHS: - /Users/you/.rvm/gems/jruby-1.5.0 - /Users/you/.rvm/gems/jruby-1.5.0@global - GEM CONFIGURATION: - :update_sources => true - :verbose => true - :benchmark => false - :backtrace => false - :bulk_threshold => 1000 - "install" => "--env-shebang" - "update" => "--env-shebang" - "gem" => "--no-rdoc --no-ri" - :sources => ["https://rubygems.org/", "http://gems.github.com/"] - REMOTE SOURCES: - https://rubygems.org/ - http://gems.github.com/ JRUBY_GEM_ENV expect(@gem_env).to receive(:shell_out!).with('/usr/weird/bin/gem env').and_return(double('jruby_gem_env', :stdout => gem_env_out)) expected = ['ruby', Gem::Platform.new('universal-java-1.6')] expect(@gem_env.gem_platforms).to eq(expected) # it should also cache the result expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache['/usr/weird/bin/gem']).to eq(expected) end it "uses the cached result for gem platforms if available" do expect(@gem_env).not_to receive(:shell_out!) expected = ['ruby', Gem::Platform.new('universal-java-1.6')] Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache['/usr/weird/bin/gem']= expected expect(@gem_env.gem_platforms).to eq(expected) end it "uses the current gem platforms when the target env is not jruby" do gem_env_out=<<-RBX_GEM_ENV RubyGems Environment: - RUBYGEMS VERSION: 1.3.6 - RUBY VERSION: 1.8.7 (2010-05-14 patchlevel 174) [x86_64-apple-darwin10.3.0] - INSTALLATION DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514 - RUBYGEMS PREFIX: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514 - RUBY EXECUTABLE: /Users/ddeleo/.rvm/rubies/rbx-1.0.0-20100514/bin/rbx - EXECUTABLE DIRECTORY: /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514/bin - RUBYGEMS PLATFORMS: - ruby - x86_64-darwin-10 - x86_64-rubinius-1.0 - GEM PATHS: - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514 - /Users/ddeleo/.rvm/gems/rbx-1.0.0-20100514@global - GEM CONFIGURATION: - :update_sources => true - :verbose => true - :benchmark => false - :backtrace => false - :bulk_threshold => 1000 - :sources => ["https://rubygems.org/", "http://gems.github.com/"] - "gem" => "--no-rdoc --no-ri" - REMOTE SOURCES: - https://rubygems.org/ - http://gems.github.com/ RBX_GEM_ENV expect(@gem_env).to receive(:shell_out!).with('/usr/weird/bin/gem env').and_return(double('rbx_gem_env', :stdout => gem_env_out)) expect(@gem_env.gem_platforms).to eq(Gem.platforms) expect(Chef::Provider::Package::Rubygems::AlternateGemEnvironment.platform_cache['/usr/weird/bin/gem']).to eq(Gem.platforms) end it "yields to a block while masquerading as a different gems platform" do original_platforms = Gem.platforms platforms_in_block = nil begin @gem_env.with_gem_platforms(['ruby', Gem::Platform.new('sparc64-java-1.7')]) do platforms_in_block = Gem.platforms raise "gem platforms should get set to the correct value even when an error occurs" end rescue RuntimeError end expect(platforms_in_block).to eq(['ruby', Gem::Platform.new('sparc64-java-1.7')]) expect(Gem.platforms).to eq(original_platforms) end end describe Chef::Provider::Package::Rubygems do let(:target_version) { nil } before(:each) do @node = Chef::Node.new @new_resource = Chef::Resource::GemPackage.new("rspec-core") @spec_version = @new_resource.version(target_version) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) # We choose detect omnibus via RbConfig::CONFIG['bindir'] in Chef::Provider::Package::Rubygems.new allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/usr/bin/ruby") # Rubygems uses this interally allow(RbConfig::CONFIG).to receive(:[]).with('arch').and_call_original @provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) end describe "when new_resource version is nil" do let(:target_version) { nil } it "target_version_already_installed? should return false so that we can search for candidates" do @provider.load_current_resource expect(@provider.target_version_already_installed?(@provider.current_resource.version, @new_resource.version)).to be_falsey end end describe "when new_resource version is current rspec version" do let(:target_version) { RSpec::Core::Version::STRING } it "triggers a gem configuration load so a later one will not stomp its config values" do # ugly, is there a better way? expect(Gem.instance_variable_get(:@configuration)).not_to be_nil end it "uses the CurrentGemEnvironment implementation when no gem_binary_path is provided" do expect(@provider.gem_env).to be_a_kind_of(Chef::Provider::Package::Rubygems::CurrentGemEnvironment) end it "uses the AlternateGemEnvironment implementation when a gem_binary_path is provided" do @new_resource.gem_binary('/usr/weird/bin/gem') provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) expect(provider.gem_env.gem_binary_location).to eq('/usr/weird/bin/gem') end it "searches for a gem binary when running on Omnibus on Unix" do platform_mock :unix do allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/chef/embedded/bin") allow(ENV).to receive(:[]).with('PATH').and_return("/usr/bin:/usr/sbin:/opt/chef/embedded/bin") allow(File).to receive(:exists?).with('/usr/bin/gem').and_return(false) allow(File).to receive(:exists?).with('/usr/sbin/gem').and_return(true) allow(File).to receive(:exists?).with('/opt/chef/embedded/bin/gem').and_return(true) # should not get here provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) expect(provider.gem_env.gem_binary_location).to eq('/usr/sbin/gem') end end it "searches for a gem binary when running on Omnibus on Windows" do platform_mock :windows do allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("d:/opscode/chef/embedded/bin") allow(ENV).to receive(:[]).with('PATH').and_return('C:\windows\system32;C:\windows;C:\Ruby186\bin;d:\opscode\chef\embedded\bin') allow(File).to receive(:exists?).with('C:\\windows\\system32\\gem').and_return(false) allow(File).to receive(:exists?).with('C:\\windows\\gem').and_return(false) allow(File).to receive(:exists?).with('C:\\Ruby186\\bin\\gem').and_return(true) allow(File).to receive(:exists?).with('d:\\opscode\\chef\\bin\\gem').and_return(false) # should not get here allow(File).to receive(:exists?).with('d:\\opscode\\chef\\embedded\\bin\\gem').and_return(false) # should not get here provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) expect(provider.gem_env.gem_binary_location).to eq('C:\Ruby186\bin\gem') end end it "smites you when you try to use a hash of install options with an explicit gem binary" do @new_resource.gem_binary('/foo/bar') @new_resource.options(:fail => :burger) expect {Chef::Provider::Package::Rubygems.new(@new_resource, @run_context)}.to raise_error(ArgumentError) end it "converts the new resource into a gem dependency" do expect(@provider.gem_dependency).to eq(Gem::Dependency.new('rspec-core', @spec_version)) @new_resource.version('~> 1.2.0') expect(@provider.gem_dependency).to eq(Gem::Dependency.new('rspec-core', '~> 1.2.0')) end describe "when determining the currently installed version" do it "sets the current version to the version specified by the new resource if that version is installed" do @provider.load_current_resource expect(@provider.current_resource.version).to eq(@spec_version) end it "sets the current version to the highest installed version if the requested version is not installed" do @new_resource.version('9000.0.2') @provider.load_current_resource expect(@provider.current_resource.version).to eq(@spec_version) end it "leaves the current version at nil if the package is not installed" do @new_resource.package_name("no-such-gem-should-exist-with-this-name") @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end end describe "when determining the candidate version to install" do it "does not query for available versions when the current version is the target version" do @provider.current_resource = @new_resource.dup expect(@provider.candidate_version).to be_nil end it "determines the candidate version by querying the remote gem servers" do @new_resource.source('http://mygems.example.com') @provider.load_current_resource @provider.current_resource.version('0.0.1') version = Gem::Version.new(@spec_version) expect(@provider.gem_env).to receive(:candidate_version_from_remote). with(Gem::Dependency.new('rspec-core', @spec_version), "http://mygems.example.com"). and_return(version) expect(@provider.candidate_version).to eq(@spec_version) end it "parses the gem's specification if the requested source is a file" do @new_resource.package_name('chef-integration-test') @new_resource.source(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @new_resource.version('>= 0') @provider.load_current_resource expect(@provider.candidate_version).to eq('0.1.0') end end describe "when installing a gem" do before do @current_resource = Chef::Resource::GemPackage.new('rspec-core') @provider.current_resource = @current_resource @gem_dep = Gem::Dependency.new('rspec-core', @spec_version) allow(@provider).to receive(:load_current_resource) end describe "in the current gem environment" do it "installs the gem via the gems api when no explicit options are used" do expect(@provider.gem_env).to receive(:install).with(@gem_dep, :sources => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem via the gems api when a remote source is provided" do @new_resource.source('http://gems.example.org') sources = ['http://gems.example.org'] expect(@provider.gem_env).to receive(:install).with(@gem_dep, :sources => sources) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem from file via the gems api when no explicit options are used" do @new_resource.source(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') expect(@provider.gem_env).to receive(:install).with(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem from file via the gems api when the package is a path and the source is nil" do @new_resource = Chef::Resource::GemPackage.new(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) @provider.current_resource = @current_resource expect(@new_resource.source).to eq(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') expect(@provider.gem_env).to receive(:install).with(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end # this catches 'gem_package "foo"' when "./foo" is a file in the cwd, and instead of installing './foo' it fetches the remote gem it "installs the gem via the gems api, when the package has no file separator characters in it, but a matching file exists in cwd" do allow(::File).to receive(:exists?).and_return(true) @new_resource.package_name('rspec-core') expect(@provider.gem_env).to receive(:install).with(@gem_dep, :sources => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem by shelling out when options are provided as a String" do @new_resource.options('-i /alt/install/location') expected ="gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" -i /alt/install/location" expect(@provider).to receive(:shell_out!).with(expected, :env => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem with rubygems.org as an added source" do @new_resource.gem_binary('/foo/bar') @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby') expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --source=#{@new_resource.source} --source=https://rubygems.org" expect(@provider).to receive(:shell_out!).with(expected, :env => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem with cleared sources and explict source when specified" do @new_resource.gem_binary('/foo/bar') @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby') @new_resource.clear_sources(true) expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --clear-sources --source=#{@new_resource.source}" expect(@provider).to receive(:shell_out!).with(expected, :env => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end context "when no version is given" do let(:target_version) { nil } it "installs the gem by shelling out when options are provided but no version is given" do @new_resource.options('-i /alt/install/location') expected ="gem install rspec-core -q --no-rdoc --no-ri -v \"#{@provider.candidate_version}\" -i /alt/install/location" expect(@provider).to receive(:shell_out!).with(expected, :env => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end end it "installs the gem via the gems api when options are given as a Hash" do @new_resource.options(:install_dir => '/alt/install/location') expect(@provider.gem_env).to receive(:install).with(@gem_dep, :sources => nil, :install_dir => '/alt/install/location') @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end describe "at a specific version" do before do @gem_dep = Gem::Dependency.new('rspec-core', @spec_version) end it "installs the gem via the gems api" do expect(@provider.gem_env).to receive(:install).with(@gem_dep, :sources => nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end end describe "at version specified with comparison operator" do it "skips install if current version satisifies requested version" do @current_resource.version("2.3.3") @new_resource.version(">=2.3.0") expect(@provider.gem_env).not_to receive(:install) @provider.run_action(:install) end it "allows user to specify gem version with fuzzy operator" do @current_resource.version("2.3.3") @new_resource.version("~>2.3.0") expect(@provider.gem_env).not_to receive(:install) @provider.run_action(:install) end end end describe "in an alternate gem environment" do it "installs the gem by shelling out to gem install" do @new_resource.gem_binary('/usr/weird/bin/gem') expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\"", :env=>nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem from file by shelling out to gem install" do @new_resource.gem_binary('/usr/weird/bin/gem') @new_resource.source(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @new_resource.version('>= 0') expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", :env=>nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end it "installs the gem from file by shelling out to gem install when the package is a path and the source is nil" do @new_resource = Chef::Resource::GemPackage.new(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') @provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context) @provider.current_resource = @current_resource @new_resource.gem_binary('/usr/weird/bin/gem') @new_resource.version('>= 0') expect(@new_resource.source).to eq(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem') expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", :env=>nil) @provider.run_action(:install) expect(@new_resource).to be_updated_by_last_action end end end describe "when uninstalling a gem" do before do @new_resource = Chef::Resource::GemPackage.new("rspec") @current_resource = @new_resource.dup @current_resource.version('1.2.3') @provider.new_resource = @new_resource @provider.current_resource = @current_resource end describe "in the current gem environment" do it "uninstalls via the api when no explicit options are used" do # pre-reqs for action_remove to actually remove the package: expect(@provider.new_resource.version).to be_nil expect(@provider.current_resource.version).not_to be_nil # the behavior we're testing: expect(@provider.gem_env).to receive(:uninstall).with('rspec', nil) @provider.action_remove end it "uninstalls via the api when options are given as a Hash" do # pre-reqs for action_remove to actually remove the package: expect(@provider.new_resource.version).to be_nil expect(@provider.current_resource.version).not_to be_nil # the behavior we're testing: @new_resource.options(:install_dir => '/alt/install/location') expect(@provider.gem_env).to receive(:uninstall).with('rspec', nil, :install_dir => '/alt/install/location') @provider.action_remove end it "uninstalls via the gem command when options are given as a String" do @new_resource.options('-i /alt/install/location') expect(@provider).to receive(:shell_out!).with("gem uninstall rspec -q -x -I -a -i /alt/install/location", :env=>nil) @provider.action_remove end it "uninstalls a specific version of a gem when a version is provided" do @new_resource.version('1.2.3') expect(@provider.gem_env).to receive(:uninstall).with('rspec', '1.2.3') @provider.action_remove end end describe "in an alternate gem environment" do it "uninstalls via the gem command" do @new_resource.gem_binary('/usr/weird/bin/gem') expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem uninstall rspec -q -x -I -a", :env=>nil) @provider.action_remove end end end end end chef-12.3.0/spec/unit/provider/package/zypper_spec.rb0000644000004100000410000002305012520074675022570 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Provider::Package::Zypper do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("cups") @current_resource = Chef::Resource::Package.new("cups") @provider = Chef::Provider::Package::Zypper.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @status = double(:stdout => "\n", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(@status) allow(@provider).to receive(:`).and_return("2.0") end describe "when loading the current package state" do it "should create a current resource with the name of the new_resource" do expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources package name to the new resources package name" do expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should run zypper info with the package name" do expect(@provider).to receive(:shell_out).with("zypper --non-interactive info #{@new_resource.package_name}").and_return(@status) @provider.load_current_resource end it "should set the installed version to nil on the current resource if zypper info installed version is (none)" do allow(@provider).to receive(:shell_out).and_return(@status) expect(@current_resource).to receive(:version).with(nil).and_return(true) @provider.load_current_resource end it "should set the installed version if zypper info has one" do status = double(:stdout => "Version: 1.0\nInstalled: Yes\n", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(status) expect(@current_resource).to receive(:version).with("1.0").and_return(true) @provider.load_current_resource end it "should set the candidate version if zypper info has one" do status = double(:stdout => "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(status) @provider.load_current_resource expect(@provider.candidate_version).to eql("1.0") end it "should raise an exception if zypper info fails" do expect(@status).to receive(:exitstatus).and_return(1) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should not raise an exception if zypper info succeeds" do expect(@status).to receive(:exitstatus).and_return(0) expect { @provider.load_current_resource }.not_to raise_error end it "should return the current resouce" do expect(@provider.load_current_resource).to eql(@current_resource) end end describe "install_package" do it "should run zypper install with the package name and version" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0") @provider.install_package("emacs", "1.0") end it "should run zypper install without gpg checks" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks install "+ "--auto-agree-with-licenses emacs=1.0") @provider.install_package("emacs", "1.0") end it "should warn about gpg checks on zypper install" do expect(Chef::Log).to receive(:warn).with( /All packages will be installed without gpg signature checks/) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks install "+ "--auto-agree-with-licenses emacs=1.0") @provider.install_package("emacs", "1.0") end end describe "upgrade_package" do it "should run zypper update with the package name and version" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0") @provider.upgrade_package("emacs", "1.0") end it "should run zypper update without gpg checks" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks install "+ "--auto-agree-with-licenses emacs=1.0") @provider.upgrade_package("emacs", "1.0") end it "should warn about gpg checks on zypper upgrade" do expect(Chef::Log).to receive(:warn).with( /All packages will be installed without gpg signature checks/) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks install "+ "--auto-agree-with-licenses emacs=1.0") @provider.upgrade_package("emacs", "1.0") end it "should run zypper upgrade without gpg checks" do expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks install "+ "--auto-agree-with-licenses emacs=1.0") @provider.upgrade_package("emacs", "1.0") end end describe "remove_package" do context "when package version is not explicitly specified" do it "should run zypper remove with the package name" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive remove emacs") @provider.remove_package("emacs", nil) end end context "when package version is explicitly specified" do it "should run zypper remove with the package name" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive remove emacs=1.0") @provider.remove_package("emacs", "1.0") end it "should run zypper remove without gpg checks" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks remove emacs=1.0") @provider.remove_package("emacs", "1.0") end it "should warn about gpg checks on zypper remove" do expect(Chef::Log).to receive(:warn).with( /All packages will be installed without gpg signature checks/) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks remove emacs=1.0") @provider.remove_package("emacs", "1.0") end end end describe "purge_package" do it "should run remove_package with the name and version" do expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0") @provider.purge_package("emacs", "1.0") end it "should run zypper purge without gpg checks" do allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0") @provider.purge_package("emacs", "1.0") end it "should warn about gpg checks on zypper purge" do expect(Chef::Log).to receive(:warn).with( /All packages will be installed without gpg signature checks/) expect(@provider).to receive(:shell_out!).with( "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0") @provider.purge_package("emacs", "1.0") end end describe "on an older zypper" do before(:each) do allow(@provider).to receive(:`).and_return("0.11.6") end describe "install_package" do it "should run zypper install with the package name and version" do expect(@provider).to receive(:shell_out!).with( "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs") @provider.install_package("emacs", "1.0") end end describe "upgrade_package" do it "should run zypper update with the package name and version" do expect(@provider).to receive(:shell_out!).with( "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs") @provider.upgrade_package("emacs", "1.0") end end describe "remove_package" do it "should run zypper remove with the package name" do expect(@provider).to receive(:shell_out!).with( "zypper --no-gpg-checks remove -y emacs") @provider.remove_package("emacs", "1.0") end end end end chef-12.3.0/spec/unit/provider/package/rpm_spec.rb0000644000004100000410000002267612520074675022052 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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::Package::Rpm do let(:provider) { Chef::Provider::Package::Rpm.new(new_resource, run_context) } let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:new_resource) do Chef::Resource::Package.new("ImageMagick-c++").tap do |resource| resource.source "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" end end let(:exitstatus) { 0 } let(:stdout) { String.new('') } let(:status) { double('Process::Status', exitstatus: exitstatus, stdout: stdout) } before(:each) do allow(::File).to receive(:exists?).and_return(true) allow(provider).to receive(:shell_out!).and_return(status) end describe "when determining the current state of the package" do it "should create a current resource with the name of new_resource" do provider.load_current_resource expect(provider.current_resource.name).to eq("ImageMagick-c++") end it "should set the current reource package name to the new resource package name" do provider.load_current_resource expect(provider.current_resource.package_name).to eq('ImageMagick-c++') end it "should raise an exception if a source is supplied but not found" do allow(::File).to receive(:exists?).and_return(false) expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package) end context "installation exists" do let(:stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" } it "should get the source package version from rpm if provided" do expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status) expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status) provider.load_current_resource expect(provider.current_resource.package_name).to eq("ImageMagick-c++") expect(provider.new_resource.version).to eq("6.5.4.7-7.el6_5") end it "should return the current version installed if found by rpm" do expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status) expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status) provider.load_current_resource expect(provider.current_resource.version).to eq("6.5.4.7-7.el6_5") end end context "source is uri formed" do before(:each) do allow(::File).to receive(:exists?).and_return(false) end %w(http HTTP https HTTPS ftp FTP).each do |scheme| it "should accept uri formed source (#{scheme})" do new_resource.source "#{scheme}://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" expect(provider.load_current_resource).not_to be_nil end end %w(file FILE).each do |scheme| it "should accept uri formed source (#{scheme})" do new_resource.source "#{scheme}:///ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" expect(provider.load_current_resource).not_to be_nil end end it "should raise an exception if an uri formed source is non-supported scheme" do new_resource.source "foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" expect(provider.load_current_resource).to be_nil expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package) end end context "source is not defiend" do let(:new_resource) { Chef::Resource::Package.new("ImageMagick-c++") } it "should raise an exception if the source is not set but we are installing" do expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package) end end context "installation does not exist" do let(:stdout) { String.new("package openssh-askpass is not installed") } let(:exitstatus) { -1 } let(:new_resource) do Chef::Resource::Package.new("openssh-askpass").tap do |resource| resource.source "openssh-askpass" end end it "should raise an exception if rpm fails to run" do allow(provider).to receive(:shell_out).and_return(status) expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package) end it "should not detect the package name as version when not installed" do expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status) expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status) provider.load_current_resource expect(provider.current_resource.version).to be_nil end end end describe "after the current resource is loaded" do let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") } let(:provider) do Chef::Provider::Package::Rpm.new(new_resource, run_context).tap do |provider| provider.current_resource = current_resource end end describe "when installing or upgrading" do it "should run rpm -i with the package source to install" do expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.install_package("ImageMagick-c++", "6.5.4.7-7.el6_5") end it "should run rpm -U with the package source to upgrade" do current_resource.version("21.4-19.el5") expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5") end it "should install package if missing and set to upgrade" do current_resource.version("ImageMagick-c++") expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5") end context "allowing downgrade" do let(:new_resource) { Chef::Resource::RpmPackage.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") } let(:current_resource) { Chef::Resource::RpmPackage.new("ImageMagick-c++") } it "should run rpm -U --oldpackage with the package source to downgrade" do new_resource.allow_downgrade(true) current_resource.version("21.4-19.el5") expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5") end end context "installing when the name is a path" do let(:new_resource) { Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") } let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") } it "should install from a path when the package is a path and the source is nil" do expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.current_resource = current_resource expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5") end it "should uprgrade from a path when the package is a path and the source is nil" do expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") current_resource.version("21.4-19.el5") provider.current_resource = current_resource expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5") end end it "installs with custom options specified in the resource" do provider.candidate_version = '11' new_resource.options("--dbpath /var/lib/rpm") expect(provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") provider.install_package(new_resource.name, provider.candidate_version) end end describe "when removing the package" do it "should run rpm -e to remove the package" do expect(provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5") provider.remove_package("ImageMagick-c++", "6.5.4.7-7.el6_5") end end end end chef-12.3.0/spec/unit/provider/package/apt_spec.rb0000644000004100000410000003640212520074675022030 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'ostruct' describe Chef::Provider::Package::Apt do # XXX: sorry this is ugly and was done quickly to get 12.0.2 out, this file needs a rewrite to use # let blocks and shared examples [ Chef::Resource::Package, Chef::Resource::AptPackage ].each do |resource_klass| describe "when the new_resource is a #{resource_klass}" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = resource_klass.new("irssi", @run_context) @status = double("Status", :exitstatus => 0) @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context) @stdin = StringIO.new @stdout =<<-PKG_STATUS irssi: Installed: (none) Candidate: 0.8.14-1ubuntu4 Version table: 0.8.14-1ubuntu4 0 500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages PKG_STATUS @stderr = "" @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0) @timeout = 900 end describe "when loading current resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out!).with( "apt-cache policy #{@new_resource.package_name}", :timeout => @timeout ).and_return(@shell_out) @provider.load_current_resource current_resource = @provider.current_resource expect(current_resource).to be_a(Chef::Resource::Package) expect(current_resource.name).to eq("irssi") expect(current_resource.package_name).to eq("irssi") expect(current_resource.version).to be_nil end it "should set the installed version if package has one" do @stdout.replace(<<-INSTALLED) sudo: Installed: 1.7.2p1-1ubuntu5.3 Candidate: 1.7.2p1-1ubuntu5.3 Version table: *** 1.7.2p1-1ubuntu5.3 0 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages 500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages 100 /var/lib/dpkg/status 1.7.2p1-1ubuntu5 0 500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages INSTALLED expect(@provider).to receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.7.2p1-1ubuntu5.3") expect(@provider.candidate_version).to eql("1.7.2p1-1ubuntu5.3") end # libmysqlclient-dev is a real package in newer versions of debian + ubuntu # list of virtual packages: http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt it "should not install the virtual package there is a single provider package and it is installed" do @new_resource.package_name("libmysqlclient15-dev") virtual_package_out=<<-VPKG_STDOUT libmysqlclient15-dev: Installed: (none) Candidate: (none) Version table: VPKG_STDOUT virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy libmysqlclient15-dev", :timeout => @timeout ).and_return(virtual_package) showpkg_out =<<-SHOWPKG_STDOUT Package: libmysqlclient15-dev Versions: Reverse Depends: libmysqlclient-dev,libmysqlclient15-dev libmysqlclient-dev,libmysqlclient15-dev libmysqlclient-dev,libmysqlclient15-dev libmysqlclient-dev,libmysqlclient15-dev libmysqlclient-dev,libmysqlclient15-dev libmysqlclient-dev,libmysqlclient15-dev Dependencies: Provides: Reverse Provides: libmysqlclient-dev 5.1.41-3ubuntu12.7 libmysqlclient-dev 5.1.41-3ubuntu12.10 libmysqlclient-dev 5.1.41-3ubuntu12 SHOWPKG_STDOUT showpkg = double(:stdout => showpkg_out,:exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache showpkg libmysqlclient15-dev", :timeout => @timeout ).and_return(showpkg) real_package_out=<<-RPKG_STDOUT libmysqlclient-dev: Installed: 5.1.41-3ubuntu12.10 Candidate: 5.1.41-3ubuntu12.10 Version table: *** 5.1.41-3ubuntu12.10 0 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main Packages 100 /var/lib/dpkg/status 5.1.41-3ubuntu12.7 0 500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages 5.1.41-3ubuntu12 0 500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages RPKG_STDOUT real_package = double(:stdout => real_package_out,:exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy libmysqlclient-dev", :timeout => @timeout ).and_return(real_package) @provider.load_current_resource end it "should raise an exception if you specify a virtual package with multiple provider packages" do @new_resource.package_name("mp3-decoder") virtual_package_out=<<-VPKG_STDOUT mp3-decoder: Installed: (none) Candidate: (none) Version table: VPKG_STDOUT virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache policy mp3-decoder", :timeout => @timeout ).and_return(virtual_package) showpkg_out=<<-SHOWPKG_STDOUT Package: mp3-decoder Versions: Reverse Depends: nautilus,mp3-decoder vux,mp3-decoder plait,mp3-decoder ecasound,mp3-decoder nautilus,mp3-decoder Dependencies: Provides: Reverse Provides: vlc-nox 1.0.6-1ubuntu1.8 vlc 1.0.6-1ubuntu1.8 vlc-nox 1.0.6-1ubuntu1 vlc 1.0.6-1ubuntu1 opencubicplayer 1:0.1.17-2 mpg321 0.2.10.6 mpg123 1.12.1-0ubuntu1 SHOWPKG_STDOUT showpkg = double(:stdout => showpkg_out,:exitstatus => 0) expect(@provider).to receive(:shell_out!).with( "apt-cache showpkg mp3-decoder", :timeout => @timeout ).and_return(showpkg) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should run apt-cache policy with the default_release option, if there is one on the resource" do @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context) @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context) allow(@new_resource).to receive(:default_release).and_return("lenny-backports") allow(@new_resource).to receive(:provider).and_return(nil) expect(@provider).to receive(:shell_out!).with( "apt-cache -o APT::Default-Release=lenny-backports policy irssi", :timeout => @timeout ).and_return(@shell_out) @provider.load_current_resource end it "raises an exception if a source is specified (CHEF-5113)" do @new_resource.source "pluto" expect(@provider).to receive(:shell_out!).with( "apt-cache policy #{@new_resource.package_name}", :timeout => @timeout ).and_return(@shell_out) @provider.load_current_resource @provider.define_resource_requirements expect(@provider).to receive(:shell_out!).with("apt-cache policy irssi", {:timeout=>900}).and_return(@shell_out) expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end end context "after loading the current resource" do before do @current_resource = resource_klass.new("irssi", @run_context) @provider.current_resource = @current_resource end describe "install_package" do it "should run apt-get install with the package name and version" do expect(@provider).to receive(:shell_out!). with( "apt-get -q -y install irssi=0.8.12-7", :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil}, :timeout => @timeout ) @provider.install_package("irssi", "0.8.12-7") end it "should run apt-get install with the package name and version and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes install irssi=0.8.12-7", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @new_resource.options("--force-yes") @provider.install_package("irssi", "0.8.12-7") end it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do @new_resource = nil @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context) @new_resource.default_release("lenny-backports") @new_resource.provider = nil @provider.new_resource = @new_resource expect(@provider).to receive(:shell_out!).with( "apt-get -q -y -o APT::Default-Release=lenny-backports install irssi=0.8.12-7", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @provider.install_package("irssi", "0.8.12-7") end end describe resource_klass, "upgrade_package" do it "should run install_package with the name and version" do expect(@provider).to receive(:install_package).with("irssi", "0.8.12-7") @provider.upgrade_package("irssi", "0.8.12-7") end end describe resource_klass, "remove_package" do it "should run apt-get remove with the package name" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y remove irssi", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil}, :timeout => @timeout ) @provider.remove_package("irssi", "0.8.12-7") end it "should run apt-get remove with the package name and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes remove irssi", :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @new_resource.options("--force-yes") @provider.remove_package("irssi", "0.8.12-7") end end describe "when purging a package" do it "should run apt-get purge with the package name" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y purge irssi", :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @provider.purge_package("irssi", "0.8.12-7") end it "should run apt-get purge with the package name and options if specified" do expect(@provider).to receive(:shell_out!).with( "apt-get -q -y --force-yes purge irssi", :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @new_resource.options("--force-yes") @provider.purge_package("irssi", "0.8.12-7") end end describe "when preseeding a package" do before(:each) do allow(@provider).to receive(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed") end it "should get the full path to the preseed response file" do file = "/tmp/irssi-0.8.12-7.seed" expect(@provider).to receive(:shell_out!).with( "debconf-set-selections /tmp/irssi-0.8.12-7.seed", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil}, :timeout => @timeout ) @provider.preseed_package(file) end it "should run debconf-set-selections on the preseed file if it has changed" do expect(@provider).to receive(:shell_out!).with( "debconf-set-selections /tmp/irssi-0.8.12-7.seed", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil}, :timeout => @timeout ) file = @provider.get_preseed_file("irssi", "0.8.12-7") @provider.preseed_package(file) end it "should not run debconf-set-selections if the preseed file has not changed" do allow(@provider).to receive(:check_all_packages_state) @current_resource.version "0.8.11" @new_resource.response_file "/tmp/file" allow(@provider).to receive(:get_preseed_file).and_return(false) expect(@provider).not_to receive(:shell_out!) @provider.run_action(:reconfig) end end describe "when reconfiguring a package" do it "should run dpkg-reconfigure package" do expect(@provider).to receive(:shell_out!).with( "dpkg-reconfigure irssi", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @provider.reconfig_package("irssi", "0.8.12-7") end end describe "when installing a virtual package" do it "should install the package without specifying a version" do @provider.is_virtual_package['libmysqlclient-dev'] = true expect(@provider).to receive(:shell_out!).with( "apt-get -q -y install libmysqlclient-dev", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @provider.install_package("libmysqlclient-dev", "not_a_real_version") end end describe "when installing multiple packages" do it "can install a virtual package followed by a non-virtual package" do # https://github.com/chef/chef/issues/2914 @provider.is_virtual_package['libmysqlclient-dev'] = true @provider.is_virtual_package['irssi'] = false expect(@provider).to receive(:shell_out!).with( "apt-get -q -y install libmysqlclient-dev irssi=0.8.12-7", :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @timeout ) @provider.install_package(["libmysqlclient-dev", "irssi"], ["not_a_real_version", "0.8.12-7"]) end end end end end end chef-12.3.0/spec/unit/provider/package/paludis_spec.rb0000644000004100000410000001244212520074675022703 0ustar www-datawww-data# # Author:: Vasiliy Tolstov # Copyright:: Copyright (c) 2014 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 'ostruct' # based on the ips specs describe Chef::Provider::Package::Paludis do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("net/ntp") @current_resource = Chef::Resource::Package.new("net/ntp") allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider = Chef::Provider::Package::Paludis.new(@new_resource, @run_context) @stdin = StringIO.new @stderr = StringIO.new @stdout =<<-PKG_STATUS group/ntp 0 accounts group/ntp 0 installed-accounts net/ntp 4.2.6_p5-r2 arbor user/ntp 0 accounts user/ntp 0 installed-accounts net/ntp 4.2.6_p5-r1 installed PKG_STATUS @pid = 12345 @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0) end context "when loading current resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources package name to the new resources package name" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should run pkg info with the package name" do expect(@provider).to receive(:shell_out!).with("cave -L warning print-ids -M none -m \"#{@new_resource.package_name}\" -f \"%c/%p %v %r\n\"").and_return(@shell_out) @provider.load_current_resource end it "should return new version if package is installed" do @stdout.replace(<<-INSTALLED) group/ntp 0 accounts group/ntp 0 installed-accounts net/ntp 4.2.6_p5-r2 arbor user/ntp 0 accounts user/ntp 0 installed-accounts net/ntp 4.2.6_p5-r1 installed INSTALLED expect(@provider).to receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource expect(@current_resource.version).to eq("4.2.6_p5-r1") expect(@provider.candidate_version).to eql("4.2.6_p5-r2") end it "should return the current resource" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) expect(@provider.load_current_resource).to eql(@current_resource) end end context "when installing a package" do it "should run pkg install with the package name and version" do expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=net/ntp-4.2.6_p5-r2\"", {:timeout=>@new_resource.timeout}) @provider.install_package("net/ntp", "4.2.6_p5-r2") end it "should run pkg install with the package name and version and options if specified" do expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x --preserve-world \"=net/ntp-4.2.6_p5-r2\"", {:timeout=>@new_resource.timeout}) allow(@new_resource).to receive(:options).and_return("--preserve-world") @provider.install_package("net/ntp", "4.2.6_p5-r2") end it "should not contain invalid characters for the version string" do @stdout.replace(<<-PKG_STATUS) sys-process/lsof 4.87 arbor sys-process/lsof 4.87 x86_64 PKG_STATUS expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=sys-process/lsof-4.87\"", {:timeout=>@new_resource.timeout}) @provider.install_package("sys-process/lsof", "4.87") end it "should not include the human-readable version in the candidate_version" do @stdout.replace(<<-PKG_STATUS) sys-process/lsof 4.87 arbor sys-process/lsof 4.87 x86_64 PKG_STATUS expect(@provider).to receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource expect(@current_resource.version).to be_nil expect(@provider.candidate_version).to eql("4.87") end end context "when upgrading a package" do it "should run pkg install with the package name and version" do expect(@provider).to receive(:shell_out!).with("cave -L warning resolve -x \"=net/ntp-4.2.6_p5-r2\"", {:timeout=>@new_resource.timeout}) @provider.upgrade_package("net/ntp", "4.2.6_p5-r2") end end context "when uninstalling a package" do it "should run pkg uninstall with the package name and version" do expect(@provider).to receive(:shell_out!).with("cave -L warning uninstall -x \"=net/ntp-4.2.6_p5-r2\"") @provider.remove_package("net/ntp", "4.2.6_p5-r2") end end end chef-12.3.0/spec/unit/provider/package/dpkg_spec.rb0000644000004100000410000001654412520074675022176 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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::Package::Dpkg do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("wget") @new_resource.source "/tmp/wget_1.11.4-1ubuntu1_amd64.deb" @provider = Chef::Provider::Package::Dpkg.new(@new_resource, @run_context) @status = double(:stdout => "", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(@status) allow(::File).to receive(:exists?).and_return(true) end describe "when loading the current resource state" do it "should create a current resource with the name of the new_resource" do @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("wget") end it "should raise an exception if a source is supplied but not found" do @provider.load_current_resource @provider.define_resource_requirements allow(::File).to receive(:exists?).and_return(false) expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end describe 'gets the source package version from dpkg-deb' do def check_version(version) @status = double(:stdout => "wget\t#{version}", :exitstatus => 0) allow(@provider).to receive(:shell_out).with("dpkg-deb -W #{@new_resource.source}").and_return(@status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("wget") expect(@new_resource.version).to eq(version) expect(@provider.candidate_version).to eq(version) end it 'if short version provided' do check_version('1.11.4') end it 'if extended version provided' do check_version('1.11.4-1ubuntu1') end it 'if distro-specific version provided' do check_version('1.11.4-1ubuntu1~lucid') end it 'returns the version if an epoch is used' do check_version('1:1.8.3-2') end end it "gets the source package name from dpkg-deb correctly when the package name has `-', `+' or `.' characters" do stdout = "f.o.o-pkg++2\t1.11.4-1ubuntu1" status = double(:stdout => stdout, :exitstatus => 1) allow(@provider).to receive(:shell_out).and_return(status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("f.o.o-pkg++2") end it "should raise an exception if the source is not set but we are installing" do @new_resource = Chef::Resource::Package.new("wget") @provider.new_resource = @new_resource @provider.load_current_resource @provider.define_resource_requirements expect { @provider.run_action(:install)}.to raise_error(Chef::Exceptions::Package) end it "should return the current version installed if found by dpkg" do stdout = <<-DPKG_S Package: wget Status: install ok installed Priority: important Section: web Installed-Size: 1944 Maintainer: Ubuntu Core developers Architecture: amd64 Version: 1.11.4-1ubuntu1 Config-Version: 1.11.4-1ubuntu1 Depends: libc6 (>= 2.8~20080505), libssl0.9.8 (>= 0.9.8f-5) Conflicts: wget-ssl DPKG_S status = double(:stdout => stdout, :exitstatus => 1) allow(@provider).to receive(:shell_out).with("dpkg -s wget").and_return(status) @provider.load_current_resource expect(@provider.current_resource.version).to eq("1.11.4-1ubuntu1") end it "should raise an exception if dpkg fails to run" do status = double(:stdout => "", :exitstatus => -1) allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end end describe Chef::Provider::Package::Dpkg, "install and upgrade" do it "should run dpkg -i with the package source" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb" ) @provider.install_package("wget", "1.11.4-1ubuntu1") end it "should run dpkg -i if the package is a path and the source is nil" do @new_resource = Chef::Resource::Package.new("/tmp/wget_1.11.4-1ubuntu1_amd64.deb") @provider = Chef::Provider::Package::Dpkg.new(@new_resource, @run_context) expect(@provider).to receive(:run_noninteractive).with( "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb" ) @provider.install_package("/tmp/wget_1.11.4-1ubuntu1_amd64.deb", "1.11.4-1ubuntu1") end it "should run dpkg -i if the package is a path and the source is nil for an upgrade" do @new_resource = Chef::Resource::Package.new("/tmp/wget_1.11.4-1ubuntu1_amd64.deb") @provider = Chef::Provider::Package::Dpkg.new(@new_resource, @run_context) expect(@provider).to receive(:run_noninteractive).with( "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb" ) @provider.upgrade_package("/tmp/wget_1.11.4-1ubuntu1_amd64.deb", "1.11.4-1ubuntu1") end it "should run dpkg -i with the package source and options if specified" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -i --force-yes /tmp/wget_1.11.4-1ubuntu1_amd64.deb" ) allow(@new_resource).to receive(:options).and_return("--force-yes") @provider.install_package("wget", "1.11.4-1ubuntu1") end it "should upgrade by running install_package" do expect(@provider).to receive(:install_package).with("wget", "1.11.4-1ubuntu1") @provider.upgrade_package("wget", "1.11.4-1ubuntu1") end end describe Chef::Provider::Package::Dpkg, "remove and purge" do it "should run dpkg -r to remove the package" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -r wget" ) @provider.remove_package("wget", "1.11.4-1ubuntu1") end it "should run dpkg -r to remove the package with options if specified" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -r --force-yes wget" ) allow(@new_resource).to receive(:options).and_return("--force-yes") @provider.remove_package("wget", "1.11.4-1ubuntu1") end it "should run dpkg -P to purge the package" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -P wget" ) @provider.purge_package("wget", "1.11.4-1ubuntu1") end it "should run dpkg -P to purge the package with options if specified" do expect(@provider).to receive(:run_noninteractive).with( "dpkg -P --force-yes wget" ) allow(@new_resource).to receive(:options).and_return("--force-yes") @provider.purge_package("wget", "1.11.4-1ubuntu1") end end end chef-12.3.0/spec/unit/provider/package/ips_spec.rb0000644000004100000410000002277512520074675022047 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'ostruct' # based on the apt specs describe Chef::Provider::Package::Ips do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("crypto/gnupg", @run_context) @current_resource = Chef::Resource::Package.new("crypto/gnupg", @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider = Chef::Provider::Package::Ips.new(@new_resource, @run_context) end def local_output stdin = StringIO.new stdout = '' stderr =<<-PKG_STATUS pkg: info: no packages matching the following patterns you specified are installed on the system. Try specifying -r to query remotely: crypto/gnupg PKG_STATUS return OpenStruct.new(:stdout => stdout,:stdin => stdin,:stderr => stderr,:status => @status,:exitstatus => 1) end def remote_output stdout = <<-PKG_STATUS Name: security/sudo Summary: sudo - authority delegation tool State: Not Installed Publisher: omnios Version: 1.8.4.1 (1.8.4p1) Build Release: 5.11 Branch: 0.151002 Packaging Date: April 1, 2012 05:55:52 PM Size: 2.57 MB FMRI: pkg://omnios/security/sudo@1.8.4.1,5.11-0.151002:20120401T175552Z PKG_STATUS stdin = StringIO.new stderr = '' return OpenStruct.new(:stdout => stdout,:stdin => stdin,:stderr => stderr,:status => @status,:exitstatus => 0) end context "when loading current resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources package name to the new resources package name" do expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) @provider.load_current_resource expect(@current_resource.package_name).to eq(@new_resource.package_name) end it "should run pkg info with the package name" do expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) @provider.load_current_resource end it "should set the installed version to nil on the current resource if package state is not installed" do expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) @provider.load_current_resource expect(@current_resource.version).to be_nil end it "should set the installed version if package has one" do local = local_output local.stdout = <<-INSTALLED Name: crypto/gnupg Summary: GNU Privacy Guard Description: A complete and free implementation of the OpenPGP Standard as defined by RFC4880. Category: Applications/System Utilities State: Installed Publisher: solaris Version: 2.0.17 Build Release: 5.11 Branch: 0.175.0.0.0.2.537 Packaging Date: October 19, 2011 09:14:50 AM Size: 8.07 MB FMRI: pkg://solaris/crypto/gnupg@2.0.17,5.11-0.175.0.0.0.2.537:20111019T091450Z INSTALLED expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) @provider.load_current_resource expect(@current_resource.version).to eq("2.0.17") end it "should return the current resource" do expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output) expect(@provider.load_current_resource).to eql(@current_resource) end end context "when installing a package" do it "should run pkg install with the package name and version" do expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17") @provider.install_package("crypto/gnupg", "2.0.17") end it "should run pkg install with the package name and version and options if specified" do expect(@provider).to receive(:shell_out).with("pkg --no-refresh install -q crypto/gnupg@2.0.17") allow(@new_resource).to receive(:options).and_return("--no-refresh") @provider.install_package("crypto/gnupg", "2.0.17") end it "should not include the human-readable version in the candidate_version" do remote = remote_output remote.stdout = <<-PKG_STATUS Name: security/sudo Summary: sudo - authority delegation tool State: Not Installed Publisher: omnios Version: 1.8.4.1 (1.8.4p1) Build Release: 5.11 Branch: 0.151002 Packaging Date: April 1, 2012 05:55:52 PM Size: 2.57 MB FMRI: pkg://omnios/security/sudo@1.8.4.1,5.11-0.151002:20120401T175552Z PKG_STATUS expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote) @provider.load_current_resource expect(@current_resource.version).to be_nil expect(@provider.candidate_version).to eql("1.8.4.1") end it "should not upgrade the package if it is already installed" do local = local_output local.stdout = <<-INSTALLED Name: crypto/gnupg Summary: GNU Privacy Guard Description: A complete and free implementation of the OpenPGP Standard as defined by RFC4880. Category: Applications/System Utilities State: Installed Publisher: solaris Version: 2.0.17 Build Release: 5.11 Branch: 0.175.0.0.0.2.537 Packaging Date: October 19, 2011 09:14:50 AM Size: 8.07 MB FMRI: pkg://solaris/crypto/gnupg@2.0.17,5.11-0.175.0.0.0.2.537:20111019T091450Z INSTALLED remote = remote_output remote.stdout = <<-REMOTE Name: crypto/gnupg Summary: GNU Privacy Guard Description: A complete and free implementation of the OpenPGP Standard as defined by RFC4880. Category: Applications/System Utilities State: Not Installed Publisher: solaris Version: 2.0.18 Build Release: 5.11 Branch: 0.175.0.0.0.2.537 Packaging Date: October 19, 2011 09:14:50 AM Size: 8.07 MB FMRI: pkg://solaris/crypto/gnupg@2.0.18,5.11-0.175.0.0.0.2.537:20111019T091450Z REMOTE expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local) expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote) expect(@provider).to receive(:install_package).exactly(0).times @provider.run_action(:install) end context "when accept_license is true" do before do allow(@new_resource).to receive(:accept_license).and_return(true) end it "should run pkg install with the --accept flag" do expect(@provider).to receive(:shell_out).with("pkg install -q --accept crypto/gnupg@2.0.17") @provider.install_package("crypto/gnupg", "2.0.17") end end end context "when upgrading a package" do it "should run pkg install with the package name and version" do expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17") @provider.upgrade_package("crypto/gnupg", "2.0.17") end end context "when uninstalling a package" do it "should run pkg uninstall with the package name and version" do expect(@provider).to receive(:shell_out!).with("pkg uninstall -q crypto/gnupg@2.0.17") @provider.remove_package("crypto/gnupg", "2.0.17") end it "should run pkg uninstall with the package name and version and options if specified" do expect(@provider).to receive(:shell_out!).with("pkg --no-refresh uninstall -q crypto/gnupg@2.0.17") allow(@new_resource).to receive(:options).and_return("--no-refresh") @provider.remove_package("crypto/gnupg", "2.0.17") end end end chef-12.3.0/spec/unit/provider/package/smartos_spec.rb0000644000004100000410000000767412520074675022745 0ustar www-datawww-data# # Author:: Trevor O (trevoro@joyent.com) # Author:: Yukihiko Sawanobori (sawanoboriyu@higanworks.com) # Copyright:: Copyright (c) 2012 Opscode # 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper")) require 'ostruct' describe Chef::Provider::Package::SmartOS, "load_current_resource" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("varnish") @current_resource = Chef::Resource::Package.new("varnish") @status = double("Status", :exitstatus => 0) @provider = Chef::Provider::Package::SmartOS.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @stdin = StringIO.new @stdout = "varnish-2.1.5nb2\n" @stderr = StringIO.new @pid = 10 @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0) end describe "when loading current resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resource package name" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) expect(@current_resource).to receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end it "should set the installed version if it is installed" do expect(@provider).to receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource expect(@current_resource.version).to eq("2.1.5nb2") end it "should set the installed version to nil if it's not installed" do out = OpenStruct.new(:stdout => nil) expect(@provider).to receive(:shell_out!).and_return(out) @provider.load_current_resource expect(@current_resource.version).to eq(nil) end end describe "candidate_version" do it "should return the candidate_version variable if already setup" do @provider.candidate_version = "2.1.1" expect(@provider).not_to receive(:shell_out!) @provider.candidate_version end it "should lookup the candidate_version if the variable is not already set" do search = double() expect(search).to receive(:each_line). and_yield("something-varnish-1.1.1 something varnish like\n"). and_yield("varnish-2.3.4 actual varnish\n") @shell_out = double('shell_out!', :stdout => search) expect(@provider).to receive(:shell_out!).with('/opt/local/bin/pkgin se varnish', :env => nil, :returns => [0,1]).and_return(@shell_out) expect(@provider.candidate_version).to eq("2.3.4") end end describe "when manipulating a resource" do it "run pkgin and install the package" do out = OpenStruct.new(:stdout => nil) expect(@provider).to receive(:shell_out!).with("/opt/local/sbin/pkg_info -E \"varnish*\"", {:env => nil, :returns=>[0,1]}).and_return(@shell_out) expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin -y install varnish-2.1.5nb2", {:env=>nil}).and_return(out) @provider.load_current_resource @provider.install_package("varnish", "2.1.5nb2") end end end chef-12.3.0/spec/unit/provider/package/homebrew_spec.rb0000644000004100000410000002500012520074675023044 0ustar www-datawww-data# # Author:: Joshua Timberman () # Copyright (c) 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Provider::Package::Homebrew do let(:node) { Chef::Node.new } let(:events) { double('Chef::Events').as_null_object } let(:run_context) { double('Chef::RunContext', node: node, events: events) } let(:new_resource) { Chef::Resource::HomebrewPackage.new('emacs') } let(:current_resource) { Chef::Resource::HomebrewPackage.new('emacs')} let(:provider) do Chef::Provider::Package::Homebrew.new(new_resource, run_context) end let(:homebrew_uid) { 1001 } let(:uninstalled_brew_info) do { 'name' => 'emacs', 'homepage' => 'http://www.gnu.org/software/emacs', 'versions' => { 'stable' => '24.3', 'bottle' => false, 'devel' => nil, 'head' => nil }, 'revision' => 0, 'installed' => [], 'linked_keg' => nil, 'keg_only' => nil, 'dependencies' => [], 'conflicts_with' => [], 'caveats' => nil, 'options' => [] } end let(:installed_brew_info) do { 'name' => 'emacs', 'homepage' => 'http://www.gnu.org/software/emacs/', 'versions' => { 'stable' => '24.3', 'bottle' => false, 'devel' => nil, 'head' => 'HEAD' }, 'revision' => 0, 'installed' => [{ 'version' => '24.3' }], 'linked_keg' => '24.3', 'keg_only' => nil, 'dependencies' => [], 'conflicts_with' => [], 'caveats' => '', 'options' => [] } end let(:keg_only_brew_info) do { 'name' => 'emacs-kegger', 'homepage' => 'http://www.gnu.org/software/emacs/', 'versions' => { 'stable' => '24.3-keggy', 'bottle' => false, 'devel' => nil, 'head' => 'HEAD' }, 'revision' => 0, 'installed' => [{ 'version' => '24.3-keggy' }], 'linked_keg' => nil, 'keg_only' => true, 'dependencies' => [], 'conflicts_with' => [], 'caveats' => '', 'options' => [] } end let(:keg_only_uninstalled_brew_info) do { 'name' => 'emacs-kegger', 'homepage' => 'http://www.gnu.org/software/emacs/', 'versions' => { 'stable' => '24.3-keggy', 'bottle' => false, 'devel' => nil, 'head' => 'HEAD' }, 'revision' => 0, 'installed' => [], 'linked_keg' => nil, 'keg_only' => true, 'dependencies' => [], 'conflicts_with' => [], 'caveats' => '', 'options' => [] } end before(:each) do end describe 'load_current_resource' do before(:each) do allow(provider).to receive(:current_installed_version).and_return(nil) allow(provider).to receive(:candidate_version).and_return('24.3') end it 'creates a current resource with the name of the new resource' do provider.load_current_resource expect(provider.current_resource).to be_a(Chef::Resource::Package) expect(provider.current_resource.name).to eql('emacs') end it 'creates a current resource with the version if the package is installed' do expect(provider).to receive(:current_installed_version).and_return('24.3') provider.load_current_resource expect(provider.current_resource.version).to eql('24.3') end it 'creates a current resource with a nil version if the package is not installed' do provider.load_current_resource expect(provider.current_resource.version).to be_nil end it 'sets a candidate version if one exists' do provider.load_current_resource expect(provider.candidate_version).to eql('24.3') end end describe 'current_installed_version' do it 'returns the latest version from brew info if the package is keg only' do allow(provider).to receive(:brew_info).and_return(keg_only_brew_info) expect(provider.current_installed_version).to eql('24.3-keggy') end it 'returns the linked keg version if the package is not keg only' do allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider.current_installed_version).to eql('24.3') end it 'returns nil if the package is not installed' do allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info) expect(provider.current_installed_version).to be_nil end it 'returns nil if the package is keg only and not installed' do allow(provider).to receive(:brew_info).and_return(keg_only_uninstalled_brew_info) expect(provider.current_installed_version).to be_nil end end describe 'brew' do before do expect(provider).to receive(:find_homebrew_uid).and_return(homebrew_uid) expect(Etc).to receive(:getpwuid).with(homebrew_uid).and_return(OpenStruct.new(:name => "name", :dir => "/")) end it 'passes a single to the brew command and return stdout' do allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => 'zombo')) expect(provider.brew).to eql('zombo') end it 'takes multiple arguments as an array' do allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => 'homestarrunner')) expect(provider.brew('info', 'opts', 'bananas')).to eql('homestarrunner') end context "when new_resource is Package" do let(:new_resource) { Chef::Resource::Package.new('emacs') } it "does not try to read homebrew_user from Package, which does not have it" do allow(provider).to receive(:shell_out!).and_return(OpenStruct.new(:stdout => 'zombo')) expect(provider.brew).to eql('zombo') end end end context 'when testing actions' do before(:each) do provider.current_resource = current_resource end describe 'install_package' do before(:each) do allow(provider).to receive(:candidate_version).and_return('24.3') end it 'installs the named package with brew install' do allow(provider.new_resource).to receive(:version).and_return('24.3') allow(provider.current_resource).to receive(:version).and_return(nil) allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info) expect(provider).to receive(:get_response_from_command).with('brew install emacs') provider.install_package('emacs', '24.3') end it 'does not do anything if the package is installed' do allow(provider.current_resource).to receive(:version).and_return('24.3') allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider).not_to receive(:get_response_from_command) provider.install_package('emacs', '24.3') end it 'uses options to the brew command if specified' do allow(provider.new_resource).to receive(:options).and_return('--cocoa') allow(provider.current_resource).to receive(:version).and_return('24.3') allow(provider).to receive(:get_response_from_command).with('brew install --cocoa emacs') provider.install_package('emacs', '24.3') end end describe 'upgrade_package' do it 'uses brew upgrade to upgrade the package if it is installed' do allow(provider.current_resource).to receive(:version).and_return('24') allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider).to receive(:get_response_from_command).with('brew upgrade emacs') provider.upgrade_package('emacs', '24.3') end it 'does not do anything if the package version is already installed' do allow(provider.current_resource).to receive(:version).and_return('24.3') allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider).not_to receive(:get_response_from_command) provider.install_package('emacs', '24.3') end it 'uses brew install to install the package if it is not installed' do allow(provider.current_resource).to receive(:version).and_return(nil) allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info) expect(provider).to receive(:get_response_from_command).with('brew install emacs') provider.upgrade_package('emacs', '24.3') end it 'uses options to the brew command if specified' do allow(provider.current_resource).to receive(:version).and_return('24') allow(provider).to receive(:brew_info).and_return(installed_brew_info) allow(provider.new_resource).to receive(:options).and_return('--cocoa') expect(provider).to receive(:get_response_from_command).with('brew upgrade --cocoa emacs') provider.upgrade_package('emacs', '24.3') end end describe 'remove_package' do it 'uninstalls the package with brew uninstall' do allow(provider.current_resource).to receive(:version).and_return('24.3') allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider).to receive(:get_response_from_command).with('brew uninstall emacs') provider.remove_package('emacs', '24.3') end it 'does not do anything if the package is not installed' do allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info) expect(provider).not_to receive(:get_response_from_command) provider.remove_package('emacs', '24.3') end end describe 'purge_package' do it 'uninstalls the package with brew uninstall --force' do allow(provider.current_resource).to receive(:version).and_return('24.3') allow(provider).to receive(:brew_info).and_return(installed_brew_info) expect(provider).to receive(:get_response_from_command).with('brew uninstall --force emacs') provider.purge_package('emacs', '24.3') end it 'does not do anything if the package is not installed' do allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info) expect(provider).not_to receive(:get_response_from_command) provider.purge_package('emacs', '24.3') end end end end chef-12.3.0/spec/unit/provider/package/solaris_spec.rb0000644000004100000410000001606312520074675022721 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Copyright:: Copyright (c) 2010 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::Package::Solaris do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("SUNWbash") @new_resource.source("/tmp/bash.pkg") @provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context) allow(::File).to receive(:exists?).and_return(true) end describe "assessing the current package status" do before do @pkginfo =<<-PKGINFO PKGINST: SUNWbash NAME: GNU Bourne-Again shell (bash) CATEGORY: system ARCH: sparc VERSION: 11.10.0,REV=2005.01.08.05.16 BASEDIR: / VENDOR: Sun Microsystems, Inc. DESC: GNU Bourne-Again shell (bash) version 3.0 PSTAMP: sfw10-patch20070430084444 INSTDATE: Nov 04 2009 01:02 HOTLINE: Please contact your local service provider PKGINFO @status = double("Status",:stdout => "", :exitstatus => 0) end it "should create a current resource with the name of new_resource" do allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource expect(@provider.current_resource.name).to eq("SUNWbash") end it "should set the current reource package name to the new resource package name" do allow(@provider).to receive(:shell_out).and_return(@status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("SUNWbash") end it "should raise an exception if a source is supplied but not found" do allow(@provider).to receive(:shell_out).and_return(@status) allow(::File).to receive(:exists?).and_return(false) @provider.load_current_resource @provider.define_resource_requirements expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Package) end it "should get the source package version from pkginfo if provided" do status = double(:stdout => @pkginfo, :exitstatus => 0) expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(status) expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(@status) @provider.load_current_resource expect(@provider.current_resource.package_name).to eq("SUNWbash") expect(@new_resource.version).to eq("11.10.0,REV=2005.01.08.05.16") end it "should return the current version installed if found by pkginfo" do status = double(:stdout => @pkginfo, :exitstatus => 0) expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(@status) expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(status) @provider.load_current_resource expect(@provider.current_resource.version).to eq("11.10.0,REV=2005.01.08.05.16") end it "should raise an exception if the source is not set but we are installing" do @new_resource = Chef::Resource::Package.new("SUNWbash") @provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context) allow(@provider).to receive(:shell_out).and_return(@status) expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end it "should raise an exception if pkginfo fails to run" do status = double(:stdout => "", :exitstatus => -1) allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package) end it "should return a current resource with a nil version if the package is not found" do expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(@status) expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(@status) @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end end describe "candidate_version" do it "should return the candidate_version variable if already setup" do @provider.candidate_version = "11.10.0,REV=2005.01.08.05.16" expect(@provider).not_to receive(:shell_out) @provider.candidate_version end it "should lookup the candidate_version if the variable is not already set" do status = double(:stdout => "", :exitstatus => 0) allow(@provider).to receive(:shell_out).and_return(status) expect(@provider).to receive(:shell_out) @provider.candidate_version end it "should throw and exception if the exitstatus is not 0" do status = double(:stdout => "", :exitstatus => 1) allow(@provider).to receive(:shell_out).and_return(status) expect { @provider.candidate_version }.to raise_error(Chef::Exceptions::Package) end end describe "install and upgrade" do it "should run pkgadd -n -d with the package source to install" do expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all") @provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16") end it "should run pkgadd -n -d when the package is a path to install" do @new_resource = Chef::Resource::Package.new("/tmp/bash.pkg") @provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context) expect(@new_resource.source).to eq("/tmp/bash.pkg") expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all") @provider.install_package("/tmp/bash.pkg", "11.10.0,REV=2005.01.08.05.16") end it "should run pkgadd -n -a /tmp/myadmin -d with the package options -a /tmp/myadmin" do allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin") expect(@provider).to receive(:shell_out!).with("pkgadd -n -a /tmp/myadmin -d /tmp/bash.pkg all") @provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16") end end describe "remove" do it "should run pkgrm -n to remove the package" do expect(@provider).to receive(:shell_out!).with("pkgrm -n SUNWbash") @provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16") end it "should run pkgrm -n -a /tmp/myadmin with options -a /tmp/myadmin" do allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin") expect(@provider).to receive(:shell_out!).with("pkgrm -n -a /tmp/myadmin SUNWbash") @provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16") end end end chef-12.3.0/spec/unit/provider/package/macports_spec.rb0000644000004100000410000002001412520074675023064 0ustar www-datawww-data# # Author:: David Balatero () # 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 'spec_helper' describe Chef::Provider::Package::Macports do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Package.new("zsh") @current_resource = Chef::Resource::Package.new("zsh") @provider = Chef::Provider::Package::Macports.new(@new_resource, @run_context) allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource) # @status = double(:stdout => "", :exitstatus => 0) # @stdin = StringIO.new # @stdout = StringIO.new # @stderr = StringIO.new # @pid = 2342 end describe "load_current_resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:current_installed_version).and_return(nil) expect(@provider).to receive(:macports_candidate_version).and_return("4.2.7") @provider.load_current_resource expect(@provider.current_resource.name).to eq("zsh") end it "should create a current resource with the version if the package is installed" do expect(@provider).to receive(:macports_candidate_version).and_return("4.2.7") expect(@provider).to receive(:current_installed_version).and_return("4.2.7") @provider.load_current_resource expect(@provider.candidate_version).to eq("4.2.7") end it "should create a current resource with a nil version if the package is not installed" do expect(@provider).to receive(:current_installed_version).and_return(nil) expect(@provider).to receive(:macports_candidate_version).and_return("4.2.7") @provider.load_current_resource expect(@provider.current_resource.version).to be_nil end it "should set a candidate version if one exists" do expect(@provider).to receive(:current_installed_version).and_return(nil) expect(@provider).to receive(:macports_candidate_version).and_return("4.2.7") @provider.load_current_resource expect(@provider.candidate_version).to eq("4.2.7") end end describe "current_installed_version" do it "should return the current version if the package is installed" do stdout = < stdout, :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.current_installed_version).to eq("0.9.8k_0") end it "should return nil if a package is not currently installed" do status = double(:stdout => " \n", :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.current_installed_version).to be_nil end end describe "macports_candidate_version" do it "should return the latest available version of a given package" do status = double(:stdout => "version: 4.2.7\n", :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.macports_candidate_version).to eq("4.2.7") end it "should return nil if there is no version for a given package" do status = double(:stdout => "Error: port fadsfadsfads not found\n", :exitstatus => 0) expect(@provider).to receive(:shell_out).and_return(status) expect(@provider.macports_candidate_version).to be_nil end end describe "install_package" do it "should run the port install command with the correct version" do expect(@current_resource).to receive(:version).and_return("4.1.6") @provider.current_resource = @current_resource expect(@provider).to receive(:shell_out!).with("port install zsh @4.2.7") @provider.install_package("zsh", "4.2.7") end it "should not do anything if a package already exists with the same version" do expect(@current_resource).to receive(:version).and_return("4.2.7") @provider.current_resource = @current_resource expect(@provider).not_to receive(:shell_out!) @provider.install_package("zsh", "4.2.7") end it "should add options to the port command when specified" do expect(@current_resource).to receive(:version).and_return("4.1.6") @provider.current_resource = @current_resource allow(@new_resource).to receive(:options).and_return("-f") expect(@provider).to receive(:shell_out!).with("port -f install zsh @4.2.7") @provider.install_package("zsh", "4.2.7") end end describe "purge_package" do it "should run the port uninstall command with the correct version" do expect(@provider).to receive(:shell_out!).with("port uninstall zsh @4.2.7") @provider.purge_package("zsh", "4.2.7") end it "should purge the currently active version if no explicit version is passed in" do expect(@provider).to receive(:shell_out!).with("port uninstall zsh") @provider.purge_package("zsh", nil) end it "should add options to the port command when specified" do allow(@new_resource).to receive(:options).and_return("-f") expect(@provider).to receive(:shell_out!).with("port -f uninstall zsh @4.2.7") @provider.purge_package("zsh", "4.2.7") end end describe "remove_package" do it "should run the port deactivate command with the correct version" do expect(@provider).to receive(:shell_out!).with("port deactivate zsh @4.2.7") @provider.remove_package("zsh", "4.2.7") end it "should remove the currently active version if no explicit version is passed in" do expect(@provider).to receive(:shell_out!).with("port deactivate zsh") @provider.remove_package("zsh", nil) end it "should add options to the port command when specified" do allow(@new_resource).to receive(:options).and_return("-f") expect(@provider).to receive(:shell_out!).with("port -f deactivate zsh @4.2.7") @provider.remove_package("zsh", "4.2.7") end end describe "upgrade_package" do it "should run the port upgrade command with the correct version" do expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6") @provider.current_resource = @current_resource expect(@provider).to receive(:shell_out!).with("port upgrade zsh @4.2.7") @provider.upgrade_package("zsh", "4.2.7") end it "should not run the port upgrade command if the version is already installed" do expect(@current_resource).to receive(:version).at_least(:once).and_return("4.2.7") @provider.current_resource = @current_resource expect(@provider).not_to receive(:shell_out!) @provider.upgrade_package("zsh", "4.2.7") end it "should call install_package if the package isn't currently installed" do expect(@current_resource).to receive(:version).at_least(:once).and_return(nil) @provider.current_resource = @current_resource expect(@provider).to receive(:install_package).and_return(true) @provider.upgrade_package("zsh", "4.2.7") end it "should add options to the port command when specified" do allow(@new_resource).to receive(:options).and_return("-f") expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6") @provider.current_resource = @current_resource expect(@provider).to receive(:shell_out!).with("port -f upgrade zsh @4.2.7") @provider.upgrade_package("zsh", "4.2.7") end end end chef-12.3.0/spec/unit/provider/template_spec.rb0000644000004100000410000000573412520074675021470 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-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 'stringio' require 'spec_helper' require 'etc' require 'ostruct' require 'support/shared/unit/provider/file' describe Chef::Provider::Template do let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } # Subject let(:provider) do provider = described_class.new(resource, run_context) allow(provider).to receive(:content).and_return(content) provider end let(:resource) do resource = Chef::Resource::Template.new("seattle", @run_context) resource.path(resource_path) resource end let(:content) do content = double('Chef::Provider::File::Content::Template', :template_location => "/foo/bar/baz") allow(File).to receive(:exists?).with("/foo/bar/baz").and_return(true) content end it_behaves_like Chef::Provider::File context "when creating the template" do let(:node) { double('Chef::Node') } let(:events) { double('Chef::Events').as_null_object } # mock all the methods let(:run_context) { double('Chef::RunContext', :node => node, :events => events) } let(:enclosing_directory) { canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates"))) } let(:resource_path) { canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt"))) } # Subject let(:provider) do provider = described_class.new(resource, run_context) allow(provider).to receive(:content).and_return(content) provider end it "stops executing when the local template source can't be found" do setup_normal_file allow(content).to receive(:template_location).and_return("/baz/bar/foo") allow(File).to receive(:exists?).with("/baz/bar/foo").and_return(false) expect { provider.run_action(:create) }.to raise_error Chef::Mixin::WhyRun::ResourceRequirements::Assertion::AssertionFailure end end it_behaves_like "a file provider with source field" end chef-12.3.0/spec/unit/provider/http_request_spec.rb0000644000004100000410000001342512520074675022400 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Provider::HttpRequest do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::HttpRequest.new('adam') @new_resource.name "adam" @new_resource.url "http://www.opscode.com/" @new_resource.message "is cool" @provider = Chef::Provider::HttpRequest.new(@new_resource, @run_context) end describe "load_current_resource" do it "should set up a Chef::REST client, with no authentication" do expect(Chef::HTTP::Simple).to receive(:new).with(@new_resource.url) @provider.load_current_resource end end describe "when making REST calls" do before(:each) do # run_action(x) forces load_current_resource to run; # that would overwrite our supplied mock Chef::Rest # object allow(@provider).to receive(:load_current_resource).and_return(true) @http = double("Chef::REST") @provider.http = @http end describe "action_get" do it "should inflate a message block at runtime" do @new_resource.message { "return" } expect(@http).to receive(:get).with("http://www.opscode.com/", {}) @provider.run_action(:get) expect(@new_resource).to be_updated end it "should run a GET request" do expect(@http).to receive(:get).with("http://www.opscode.com/", {}) @provider.run_action(:get) expect(@new_resource).to be_updated end end describe "action_put" do it "should run a PUT request with the message as the payload" do expect(@http).to receive(:put).with("http://www.opscode.com/", @new_resource.message, {}) @provider.run_action(:put) expect(@new_resource).to be_updated end it "should inflate a message block at runtime" do allow(@new_resource).to receive(:message).and_return(lambda { "return" }) expect(@http).to receive(:put).with("http://www.opscode.com/", "return", {}) @provider.run_action(:put) expect(@new_resource).to be_updated end end describe "action_post" do it "should run a PUT request with the message as the payload" do expect(@http).to receive(:post).with("http://www.opscode.com/", @new_resource.message, {}) @provider.run_action(:post) expect(@new_resource).to be_updated end it "should inflate a message block at runtime" do @new_resource.message { "return" } expect(@http).to receive(:post).with("http://www.opscode.com/", "return", {}) @provider.run_action(:post) expect(@new_resource).to be_updated end end describe "action_delete" do it "should run a DELETE request" do expect(@http).to receive(:delete).with("http://www.opscode.com/", {}) @provider.run_action(:delete) expect(@new_resource).to be_updated end end # CHEF-4762: we expect a nil return value for a "200 Success" response # and false for a "304 Not Modified" response describe "action_head" do before do @provider.http = @http end it "should inflate a message block at runtime" do @new_resource.message { "return" } expect(@http).to receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) expect(@new_resource).to be_updated end it "should run a HEAD request" do expect(@http).to receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) expect(@new_resource).to be_updated end it "should update a HEAD request with empty string response body (CHEF-4762)" do expect(@http).to receive(:head).with("http://www.opscode.com/", {}).and_return("") @provider.run_action(:head) expect(@new_resource).to be_updated end it "should update a HEAD request with nil response body (CHEF-4762)" do expect(@http).to receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) expect(@new_resource).to be_updated end it "should not update a HEAD request if a not modified response (CHEF-4762)" do if_modified_since = File.mtime(__FILE__).httpdate @new_resource.headers "If-Modified-Since" => if_modified_since expect(@http).to receive(:head).with("http://www.opscode.com/", {"If-Modified-Since" => if_modified_since}).and_return(false) @provider.run_action(:head) expect(@new_resource).not_to be_updated end it "should run a HEAD request with If-Modified-Since header" do @new_resource.headers "If-Modified-Since" => File.mtime(__FILE__).httpdate expect(@http).to receive(:head).with("http://www.opscode.com/", @new_resource.headers) @provider.run_action(:head) end it "doesn't call converge_by if HEAD does not return modified" do expect(@http).to receive(:head).and_return(false) expect(@provider).not_to receive(:converge_by) @provider.run_action(:head) end end end end chef-12.3.0/spec/unit/provider/ohai_spec.rb0000644000004100000410000000555012520074675020571 0ustar www-datawww-data# # Author:: Michael Leinartas () # Copyright:: Copyright (c) 2010 Michael Leinartas # 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/run_context' describe Chef::Provider::Ohai do before(:each) do # Copied from client_spec @fqdn = "hostname.domainname" @hostname = "hostname" @platform = "example-platform" @platform_version = "example-platform" Chef::Config[:node_name] = @fqdn mock_ohai = { :fqdn => @fqdn, :hostname => @hostname, :platform => @platform, :platform_version => @platform_version, :data => { :origdata => "somevalue" }, :data2 => { :origdata => "somevalue", :newdata => "somevalue" } } allow(mock_ohai).to receive(:all_plugins).and_return(true) allow(mock_ohai).to receive(:data).and_return(mock_ohai[:data], mock_ohai[:data2]) allow(Ohai::System).to receive(:new).and_return(mock_ohai) allow(Chef::Platform).to receive(:find_platform_and_version).and_return({ "platform" => @platform, "platform_version" => @platform_version}) # Fake node with a dummy save @node = Chef::Node.new @node.name(@fqdn) allow(@node).to receive(:save).and_return(@node) @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::Ohai.new("ohai_reload") ohai = Ohai::System.new ohai.all_plugins @node.consume_external_attrs(ohai.data,{}) @provider = Chef::Provider::Ohai.new(@new_resource, @run_context) end describe "when reloading ohai" do before do @node.automatic_attrs[:origdata] = 'somevalue' end it "applies updated ohai data to the node" do expect(@node[:origdata]).to eq('somevalue') expect(@node[:newdata]).to be_nil @provider.run_action(:reload) expect(@node[:origdata]).to eq('somevalue') expect(@node[:newdata]).to eq('somevalue') end it "should reload a specific plugin and cause node to pick up new values" do @new_resource.plugin "someplugin" @provider.run_action(:reload) expect(@node[:origdata]).to eq('somevalue') expect(@node[:newdata]).to eq('somevalue') end end end chef-12.3.0/spec/unit/provider/powershell_spec.rb0000644000004100000410000000237612520074675022040 0ustar www-datawww-data# # Author:: Adam Edwards () # 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::PowershellScript, "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::PowershellScript.new('run some powershell code', @run_context) @provider = Chef::Provider::PowershellScript.new(@new_resource, @run_context) end it "should set the -File flag as the last flag" do expect(@provider.flags.split(' ').pop).to eq("-File") end end chef-12.3.0/spec/unit/provider/ruby_block_spec.rb0000644000004100000410000000313512520074675022001 0ustar www-datawww-data# # Author:: AJ Christensen () # Copyright:: Copyright (c) 2009 Opscode # 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::RubyBlock, "initialize" do before(:each) do $evil_global_evil_laugh = :wahwah @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::RubyBlock.new("bloc party") @new_resource.block { $evil_global_evil_laugh = :mwahahaha} @provider = Chef::Provider::RubyBlock.new(@new_resource, @run_context) end it "should call the block and flag the resource as updated" do @provider.run_action(:run) expect($evil_global_evil_laugh).to eq(:mwahahaha) expect(@new_resource).to be_updated end it "accepts `create' as an alias for `run'" do # SEE ALSO: CHEF-3500 # "create" used to be the default action, it was renamed. @provider.run_action(:create) expect($evil_global_evil_laugh).to eq(:mwahahaha) expect(@new_resource).to be_updated end end chef-12.3.0/spec/unit/audit/0000755000004100000410000000000012520074675015561 5ustar www-datawww-datachef-12.3.0/spec/unit/audit/audit_event_proxy_spec.rb0000644000004100000410000002242112520074675022671 0ustar www-datawww-data# # Author:: Tyler Ball () # Author:: Claire McQuin () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/audit/audit_event_proxy' describe Chef::Audit::AuditEventProxy do let(:stdout) { StringIO.new } let(:events) { double("Chef::Events") } let(:audit_event_proxy) { Chef::Audit::AuditEventProxy.new(stdout) } before do Chef::Audit::AuditEventProxy.events = events end describe "#example_group_started" do let(:description) { "poots" } let(:group) { double("ExampleGroup", :parent_groups => parents, :description => description) } let(:notification) { double("Notification", :group => group) } context "when notified from a top-level example group" do let(:parents) { [double("ExampleGroup")] } it "notifies control_group_started event" do expect(Chef::Log).to receive(:debug). with("Entered \`control_group\` block named poots") expect(events).to receive(:control_group_started). with(description) audit_event_proxy.example_group_started(notification) end end context "when notified from an inner-level example group" do let(:parents) { [double("ExampleGroup"), double("OuterExampleGroup")] } it "does nothing" do expect(events).to_not receive(:control_group_started) audit_event_proxy.example_group_started(notification) end end end describe "#stop" do let(:examples) { [] } let(:notification) { double("Notification", :examples => examples) } let(:exception) { nil } let(:example) { double("Example", :exception => exception) } let(:control_group_name) { "audit test" } let(:control_data) { double("ControlData") } before do allow(Chef::Log).to receive(:info) # silence messages to output stream end it "sends a message that audits completed" do expect(Chef::Log).to receive(:info).with("Successfully executed all \`control_group\` blocks and contained examples") audit_event_proxy.stop(notification) end context "when an example succeeded" do let(:examples) { [example] } let(:excpetion) { nil } before do allow(audit_event_proxy).to receive(:build_control_from). with(example). and_return([control_group_name, control_data]) end it "notifies events" do expect(events).to receive(:control_example_success). with(control_group_name, control_data) audit_event_proxy.stop(notification) end end context "when an example failed" do let(:examples) { [example] } let(:exception) { double("ExpectationNotMet") } before do allow(audit_event_proxy).to receive(:build_control_from). with(example). and_return([control_group_name, control_data]) end it "notifies events" do expect(events).to receive(:control_example_failure). with(control_group_name, control_data, exception) audit_event_proxy.stop(notification) end end describe "#build_control_from" do let(:examples) { [example] } let(:example) { double("Example", :metadata => metadata, :description => example_description, :full_description => full_description, :exception => nil) } let(:metadata) { { :described_class => described_class, :example_group => example_group, :line_number => line } } let(:example_group) { { :description => group_description, :parent_example_group => parent_group } } let(:parent_group) { { :description => control_group_name, :parent_example_group => nil } } let(:line) { 27 } let(:control_data) { { :name => example_description, :desc => full_description, :resource_type => resource_type, :resource_name => resource_name, :context => context, :line_number => line } } shared_examples "built control" do before do if described_class allow(described_class).to receive(:instance_variable_get). with(:@name). and_return(resource_name) allow(described_class.class).to receive(:name). and_return(described_class.class) end end it "returns the controls block name and example metadata for reporting" do expect(events).to receive(:control_example_success). with(control_group_name, control_data) audit_event_proxy.stop(notification) end end describe "a top-level example" do # controls "port 111" do # it "has nobody listening" do # expect(port("111")).to_not be_listening # end # end # Description parts let(:group_description) { "port 111" } let(:example_description) { "has nobody listening" } let(:full_description) { group_description + " " + example_description } # Metadata fields let(:described_class) { nil } # Example group (metadata[:example_group]) fields let(:parent_group) { nil } # Expected returns let(:control_group_name) { group_description } # Control data fields let(:resource_type) { nil } let(:resource_name) { nil } let(:context) { [] } include_examples "built control" end describe "an example with an implicit subject" do # controls "application ports" do # control port(111) do # it { is_expected.to_not be_listening } # end # end # Description parts let(:control_group_name) { "application ports" } let(:group_description) { "#{resource_type} #{resource_name}" } let(:example_description) { "should not be listening" } let(:full_description) { [control_group_name, group_description, example_description].join(" ") } # Metadata fields let(:described_class) { double("Serverspec::Type::Port", :class => "Serverspec::Type::Port", :name => resource_name) } # Control data fields let(:resource_type) { "Port" } let(:resource_name) { "111" } let(:context) { [] } include_examples "built control" end describe "an example in a nested context" do # controls "application ports" do # control "port 111" do # it "is not listening" do # expect(port(111)).to_not be_listening # end # end # end # Description parts let(:control_group_name) { "application ports" } let(:group_description) { "port 111" } let(:example_description) { "is not listening" } let(:full_description) { [control_group_name, group_description, example_description].join(" ") } # Metadata fields let(:described_class) { nil } # Control data fields let(:resource_type) { nil } let(:resource_name) { nil } let(:context) { [group_description] } include_examples "built control" end describe "an example in a nested context including Serverspec" do # controls "application directory" do # control file("/tmp/audit") do # describe file("/tmp/audit/test_file") do # it "is a file" do # expect(subject).to be_file # end # end # end # end # Description parts let(:control_group_name) { "application directory" } let(:outer_group_description) { "File \"tmp/audit\"" } let(:group_description) { "#{resource_type} #{resource_name}" } let(:example_description) { "is a file" } let(:full_description) { [control_group_name, outer_group_description, group_description, example_description].join(" ") } # Metadata parts let(:described_class) { double("Serverspec::Type::File", :class => "Serverspec::Type::File", :name => resource_name) } # Example group parts let(:parent_group) { { :description => outer_group_description, :parent_example_group => control_group } } let(:control_group) { { :description => control_group_name, :parent_example_group => nil } } # Control data parts let(:resource_type) { "File" } let(:resource_name) { "/tmp/audit/test_file" } let(:context) { [outer_group_description] } include_examples "built control" end end end end chef-12.3.0/spec/unit/audit/rspec_formatter_spec.rb0000644000004100000410000000172512520074675022324 0ustar www-datawww-data# # Author:: Tyler Ball () # Author:: Claire McQuin () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/audit/rspec_formatter' describe Chef::Audit::RspecFormatter do let(:formatter) { Chef::Audit::RspecFormatter.new(nil) } it "should respond to close" do expect(formatter).to respond_to(:close) end end chef-12.3.0/spec/unit/audit/runner_spec.rb0000644000004100000410000001167012520074675020436 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'rspec/core/sandbox' require 'chef/audit/runner' require 'chef/audit/audit_event_proxy' require 'chef/audit/rspec_formatter' require 'rspec/support/spec/in_sub_process' require 'rspec/support/spec/stderr_splitter' describe Chef::Audit::Runner do include RSpec::Support::InSubProcess let(:events) { double("events") } let(:run_context) { instance_double(Chef::RunContext, :events => events) } let(:runner) { Chef::Audit::Runner.new(run_context) } around(:each) do |ex| RSpec::Core::Sandbox.sandboxed { ex.run } end context "when we run in audit mode" do let(:paths) { [ "/opt/chef/lib/chef/", 'C:\windows/here/lib/chef/' , "/opt/chef/extra/folders/lib/chef/"] } it "excludes the current path from backtrace" do paths.each do |path| expect(runner.exclusion_pattern).to match(path) end end end describe "#initialize" do it "correctly sets the run_context during initialization" do expect(runner.instance_variable_get(:@run_context)).to eq(run_context) end end context "during #run" do describe "#setup" do let(:log_location) { File.join(Dir.tmpdir, 'audit_log') } let(:color) { false } before do Chef::Config[:log_location] = log_location Chef::Config[:color] = color end it "sets all the config values" do # This runs the Serverspec includes - we don't want these hanging around in all subsequent tests so # we run this in a forked process. Keeps Serverspec files from getting loaded into main process. in_sub_process do runner.send(:setup) expect(RSpec.configuration.output_stream).to eq(log_location) expect(RSpec.configuration.error_stream).to eq(log_location) expect(RSpec.configuration.formatters.size).to eq(2) expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::AuditEventProxy)) expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::RspecFormatter)) expect(Chef::Audit::AuditEventProxy.class_variable_get(:@@events)).to eq(run_context.events) expect(RSpec.configuration.expectation_frameworks).to eq([RSpec::Matchers]) expect(RSpec::Matchers.configuration.syntax).to eq([:expect]) expect(RSpec.configuration.color).to eq(color) expect(RSpec.configuration.expose_dsl_globally?).to eq(false) expect(RSpec.configuration.backtrace_exclusion_patterns).to include(runner.exclusion_pattern) expect(Specinfra.configuration.backend).to eq(:exec) end end end describe "#register_control_groups" do let(:audits) { [] } let(:run_context) { instance_double(Chef::RunContext, :audits => audits) } it "adds the control group aliases" do runner.send(:register_control_groups) expect(RSpec::Core::DSL.example_group_aliases).to include(:__control_group__) expect(RSpec::Core::DSL.example_group_aliases).to include(:control) end context "audits exist" do let(:audits) { {"audit_name" => group} } let(:group) {Struct.new(:args, :block).new(["group_name"], nil)} it "sends the audits to the world" do runner.send(:register_control_groups) expect(RSpec.world.example_groups.size).to eq(1) # For whatever reason, `kind_of` is not working # expect(RSpec.world.example_groups).to include(kind_of(RSpec::Core::ExampleGroup)) => FAIL g = RSpec.world.example_groups[0] expect(g.ancestors).to include(RSpec::Core::ExampleGroup) expect(g.description).to eq("group_name") end end end describe "#do_run" do let(:rspec_runner) { instance_double(RSpec::Core::Runner) } it "executes the runner" do expect(RSpec::Core::Runner).to receive(:new).with(nil).and_return(rspec_runner) expect(rspec_runner).to receive(:run_specs).with([]) runner.send(:do_run) end end end describe "counters" do it "correctly calculates failed?" do expect(runner.failed?).to eq(false) end it "correctly calculates num_failed" do expect(runner.num_failed).to eq(0) end it "correctly calculates num_total" do expect(runner.num_total).to eq(0) end end end chef-12.3.0/spec/unit/audit/control_group_data_spec.rb0000644000004100000410000003315012520074675023007 0ustar www-datawww-data# # Author:: Tyler Ball () # Author:: Claire McQuin () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'securerandom' describe Chef::Audit::AuditData do let(:node_name) { "noodles" } let(:run_id) { SecureRandom.uuid } let(:audit_data) { described_class.new(node_name, run_id) } let(:control_group_1) { double("control group 1") } let(:control_group_2) { double("control group 2") } describe "#add_control_group" do context "when no control groups have been added" do it "stores the control group" do audit_data.add_control_group(control_group_1) expect(audit_data.control_groups).to include(control_group_1) end end context "when adding additional control groups" do before do audit_data.add_control_group(control_group_1) end it "stores the control group" do audit_data.add_control_group(control_group_2) expect(audit_data.control_groups).to include(control_group_2) end it "stores all control groups" do audit_data.add_control_group(control_group_2) expect(audit_data.control_groups).to include(control_group_1) end end end describe "#to_hash" do let(:audit_data_hash) { audit_data.to_hash } it "returns a hash" do expect(audit_data_hash).to be_a(Hash) end it "describes a Chef::Audit::AuditData object" do keys = [:node_name, :run_id, :start_time, :end_time, :control_groups] expect(audit_data_hash.keys).to match_array(keys) end describe ":control_groups" do let(:control_hash_1) { {:name => "control group 1"} } let(:control_hash_2) { {:name => "control group 2"} } let(:control_groups) { audit_data_hash[:control_groups] } context "with no control groups added" do it "is an empty list" do expect(control_groups).to eq [] end end context "with one control group added" do before do allow(audit_data).to receive(:control_groups).and_return([control_group_1]) end it "is a one-element list containing the control group hash" do expect(control_group_1).to receive(:to_hash).once.and_return(control_hash_1) expect(control_groups.size).to eq 1 expect(control_groups).to include(control_hash_1) end end context "with multiple control groups added" do before do allow(audit_data).to receive(:control_groups).and_return([control_group_1, control_group_2]) end it "is a list of control group hashes" do expect(control_group_1).to receive(:to_hash).and_return(control_hash_1) expect(control_group_2).to receive(:to_hash).and_return(control_hash_2) expect(control_groups.size).to eq 2 expect(control_groups).to include(control_hash_1) expect(control_groups).to include(control_hash_2) end end end end end describe Chef::Audit::ControlData do let(:name) { "ramen" } let(:resource_type) { double("Service") } let(:resource_name) { "mysql" } let(:context) { nil } let(:line_number) { 27 } let(:control_data) { described_class.new(name: name, resource_type: resource_type, resource_name: resource_name, context: context, line_number: line_number) } describe "#to_hash" do let(:control_data_hash) { control_data.to_hash } it "returns a hash" do expect(control_data_hash).to be_a(Hash) end it "describes a Chef::Audit::ControlData object" do keys = [:name, :resource_type, :resource_name, :context, :status, :details] expect(control_data_hash.keys).to match_array(keys) end context "when context is nil" do it "sets :context to an empty array" do expect(control_data_hash[:context]).to eq [] end end context "when context is non-nil" do let(:context) { ["outer"] } it "sets :context to its value" do expect(control_data_hash[:context]).to eq context end end end end describe Chef::Audit::ControlGroupData do let(:name) { "balloon" } let(:control_group_data) { described_class.new(name) } shared_context "control data" do let(:name) { "" } let(:resource_type) { nil } let(:resource_name) { nil } let(:context) { nil } let(:line_number) { 0 } let(:control_data) { { :name => name, :resource_type => resource_type, :resource_name => resource_name, :context => context, :line_number => line_number } } end shared_context "control" do include_context "control data" let(:control) { Chef::Audit::ControlData.new(name: name, resource_type: resource_type, resource_name: resource_name, context: context, line_number: line_number) } before do allow(Chef::Audit::ControlData).to receive(:new). with(name: name, resource_type: resource_type, resource_name: resource_name, context: context, line_number: line_number). and_return(control) end end describe "#new" do it "has status \"success\"" do expect(control_group_data.status).to eq "success" end end describe "#example_success" do include_context "control" def notify_success control_group_data.example_success(control_data) end it "increments the number of successful audits" do num_success = control_group_data.number_succeeded notify_success expect(control_group_data.number_succeeded).to eq (num_success + 1) end it "does not increment the number of failed audits" do num_failed = control_group_data.number_failed notify_success expect(control_group_data.number_failed).to eq (num_failed) end it "marks the audit's status as success" do notify_success expect(control.status).to eq "success" end it "does not modify its own status" do expect(control_group_data).to_not receive(:status=) status = control_group_data.status notify_success expect(control_group_data.status).to eq status end it "saves the control" do controls = control_group_data.controls expect(controls).to_not include(control) notify_success expect(controls).to include(control) end end describe "#example_failure" do include_context "control" let(:details) { "poop" } def notify_failure control_group_data.example_failure(control_data, details) end it "does not increment the number of successful audits" do num_success = control_group_data.number_succeeded notify_failure expect(control_group_data.number_succeeded).to eq num_success end it "increments the number of failed audits" do num_failed = control_group_data.number_failed notify_failure expect(control_group_data.number_failed).to eq (num_failed + 1) end it "marks the audit's status as failure" do notify_failure expect(control.status).to eq "failure" end it "marks its own status as failure" do notify_failure expect(control_group_data.status).to eq "failure" end it "saves the control" do controls = control_group_data.controls expect(controls).to_not include(control) notify_failure expect(controls).to include(control) end context "when details are not provided" do let(:details) { nil } it "does not save details to the control" do default_details = control.details expect(control).to_not receive(:details=) notify_failure expect(control.details).to eq default_details end end context "when details are provided" do let(:details) { "yep that didn't work" } it "saves details to the control" do notify_failure expect(control.details).to eq details end end end shared_examples "multiple audits" do |success_or_failure| include_context "control" let(:num_success) { 0 } let(:num_failure) { 0 } before do if num_failure == 0 num_success.times { control_group_data.example_success(control_data) } elsif num_success == 0 num_failure.times { control_group_data.example_failure(control_data, nil) } end end it "counts the number of successful audits" do expect(control_group_data.number_succeeded).to eq num_success end it "counts the number of failed audits" do expect(control_group_data.number_failed).to eq num_failure end it "marks its status as \"#{success_or_failure}\"" do expect(control_group_data.status).to eq success_or_failure end end context "when all audits pass" do include_examples "multiple audits", "success" do let(:num_success) { 3 } end end context "when one audit fails" do shared_examples "mixed audit results" do include_examples "multiple audits", "failure" do let(:audit_results) { [] } let(:num_success) { audit_results.count("success") } let(:num_failure) { 1 } before do audit_results.each do |result| if result == "success" control_group_data.example_success(control_data) else control_group_data.example_failure(control_data, nil) end end end end end context "and it's the first audit" do include_examples "mixed audit results" do let(:audit_results) { ["failure", "success", "success"] } end end context "and it's an audit in the middle" do include_examples "mixed audit results" do let(:audit_results) { ["success", "failure", "success"] } end end context "and it's the last audit" do include_examples "mixed audit results" do let(:audit_results) { ["success", "success", "failure"] } end end end context "when all audits fail" do include_examples "multiple audits", "failure" do let(:num_failure) { 3 } end end describe "#to_hash" do let(:control_group_data_hash) { control_group_data.to_hash } it "returns a hash" do expect(control_group_data_hash).to be_a(Hash) end it "describes a Chef::Audit::ControlGroupData object" do keys = [:name, :status, :number_succeeded, :number_failed, :controls, :id] expect(control_group_data_hash.keys).to match_array(keys) end describe ":controls" do let(:control_group_controls) { control_group_data_hash[:controls] } context "with no controls added" do it "is an empty list" do expect(control_group_controls).to eq [] end end context "with one control added" do include_context "control" let(:control_list) { [control_data] } let(:control_hash) { control.to_hash } before do expect(control_group_data).to receive(:controls).twice.and_return(control_list) expect(control_data).to receive(:to_hash).and_return(control_hash) end it "is a one-element list containing the control hash" do expect(control_group_controls.size).to eq 1 expect(control_group_controls).to include(control_hash) end it "adds a sequence number to the control" do control_group_data.to_hash expect(control_hash).to have_key(:sequence_number) end end context "with multiple controls added" do let(:control_hash_1) { {:line_number => 27} } let(:control_hash_2) { {:line_number => 13} } let(:control_hash_3) { {:line_number => 35} } let(:control_1) { double("control 1", :line_number => control_hash_1[:line_number], :to_hash => control_hash_1) } let(:control_2) { double("control 2", :line_number => control_hash_2[:line_number], :to_hash => control_hash_2) } let(:control_3) { double("control 3", :line_number => control_hash_3[:line_number], :to_hash => control_hash_3) } let(:control_list) { [control_1, control_2, control_3] } let(:ordered_control_hashes) { [control_hash_2, control_hash_1, control_hash_3] } before do # Another way to do this would be to call #example_success # or #example_failure per control hash, but we'd have to # then stub #create_control and it's a lot of extra stubbing work. # We can't stub the controls reader to return a list of # controls because of the call to sort! and the following # reading of controls. control_group_data.instance_variable_set(:@controls, control_list) end it "is a list of control group hashes ordered by line number" do expect(control_group_controls.size).to eq 3 expect(control_group_controls).to eq ordered_control_hashes end it "assigns sequence numbers in order" do control_group_data.to_hash ordered_control_hashes.each_with_index do |control_hash, idx| expect(control_hash[:sequence_number]).to eq idx + 1 end end end end end end chef-12.3.0/spec/unit/audit/audit_reporter_spec.rb0000644000004100000410000003134012520074675022151 0ustar www-datawww-data# # Author:: Tyler Ball () # Author:: Claire McQuin () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Audit::AuditReporter do let(:rest) { double("rest") } let(:reporter) { described_class.new(rest) } let(:node) { double("node", :name => "sofreshsoclean") } let(:run_id) { 0 } let(:start_time) { Time.new(2014, 12, 3, 9, 31, 05, "-08:00") } let(:end_time) { Time.new(2014, 12, 3, 9, 36, 14, "-08:00") } let(:run_status) { instance_double(Chef::RunStatus, :node => node, :run_id => run_id, :start_time => start_time, :end_time => end_time) } describe "#audit_phase_start" do it "notifies audit phase start to debug log" do expect(Chef::Log).to receive(:debug).with(/Audit Reporter starting/) reporter.audit_phase_start(run_status) end it "initializes an AuditData object" do expect(Chef::Audit::AuditData).to receive(:new).with(run_status.node.name, run_status.run_id) reporter.audit_phase_start(run_status) end it "saves the run status" do reporter.audit_phase_start(run_status) expect(reporter.instance_variable_get(:@run_status)).to eq run_status end end describe "#run_completed" do let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) } let(:run_data) { audit_data.to_hash } before do allow(reporter).to receive(:auditing_enabled?).and_return(true) allow(reporter).to receive(:run_status).and_return(run_status) allow(rest).to receive(:create_url).and_return(true) allow(rest).to receive(:post).and_return(true) allow(reporter).to receive(:audit_data).and_return(audit_data) allow(reporter).to receive(:run_status).and_return(run_status) allow(audit_data).to receive(:to_hash).and_return(run_data) end describe "a successful run with auditing enabled" do it "sets run start and end times" do iso_start_time = "2014-12-03T17:31:05Z" iso_end_time = "2014-12-03T17:36:14Z" reporter.run_completed(node) expect(audit_data.start_time).to eq iso_start_time expect(audit_data.end_time).to eq iso_end_time end it "posts audit data to server endpoint" do endpoint = "api.opscode.us/orgname/controls" headers = { 'X-Ops-Audit-Report-Protocol-Version' => Chef::Audit::AuditReporter::PROTOCOL_VERSION } expect(rest).to receive(:create_url). with("controls"). and_return(endpoint) expect(rest).to receive(:post). with(endpoint, run_data, headers) reporter.run_completed(node) end context "when unable to post to server" do let(:error) do e = StandardError.new e.set_backtrace(caller) e end before do expect(rest).to receive(:post).and_raise(error) allow(error).to receive(:respond_to?).and_call_original end context "the error is an http error" do let(:response) { double("response", :code => code) } before do expect(Chef::Log).to receive(:debug).with(/Sending audit report/) expect(Chef::Log).to receive(:debug).with(/Audit Report/) allow(error).to receive(:response).and_return(response) expect(error).to receive(:respond_to?).with(:response).and_return(true) end context "when the code is 404" do let(:code) { "404" } it "logs that the server doesn't support audit reporting" do expect(Chef::Log).to receive(:debug).with(/Server doesn't support audit reporting/) reporter.run_completed(node) end end shared_examples "non-404 error code" do it "saves the error report" do expect(Chef::FileCache).to receive(:store). with("failed-audit-data.json", an_instance_of(String), 0640). and_return(true) expect(Chef::FileCache).to receive(:load). with("failed-audit-data.json", false). and_return(true) expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/) reporter.run_completed(node) end end context "when the code is not 404" do include_examples "non-404 error code" do let(:code) { "505" } end end context "when there is no code" do include_examples "non-404 error code" do let(:code) { nil } end end end context "the error is not an http error" do it "logs the error" do expect(error).to receive(:respond_to?).with(:response).and_return(false) expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/) reporter.run_completed(node) end end context "when reporting url fatals are enabled" do before do allow(Chef::Config).to receive(:[]). with(:enable_reporting_url_fatals). and_return(true) end it "raises the error" do expect(error).to receive(:respond_to?).with(:response).and_return(false) allow(Chef::Log).to receive(:error).and_return(true) expect(Chef::Log).to receive(:error).with(/Reporting fatals enabled. Aborting run./) expect{ reporter.run_completed(node) }.to raise_error(error) end end end end context "when auditing is not enabled" do before do allow(Chef::Log).to receive(:debug) end it "doesn't send reports" do expect(reporter).to receive(:auditing_enabled?).and_return(false) expect(Chef::Log).to receive(:debug).with("Audit Reports are disabled. Skipping sending reports.") reporter.run_completed(node) end end context "when the run fails before audits" do before do allow(Chef::Log).to receive(:debug) end it "doesn't send reports" do expect(reporter).to receive(:auditing_enabled?).and_return(true) expect(reporter).to receive(:run_status).and_return(nil) expect(Chef::Log).to receive(:debug).with("Run failed before audit mode was initialized, not sending audit report to server") reporter.run_completed(node) end end end describe "#run_failed" do let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) } let(:run_data) { audit_data.to_hash } let(:error) { double("AuditError", :class => "Chef::Exception::AuditError", :message => "Well that certainly didn't work", :backtrace => ["line 0", "line 1", "line 2"]) } before do allow(reporter).to receive(:auditing_enabled?).and_return(true) allow(reporter).to receive(:run_status).and_return(run_status) allow(reporter).to receive(:audit_data).and_return(audit_data) allow(audit_data).to receive(:to_hash).and_return(run_data) end it "adds the error information to the reported data" do expect(rest).to receive(:create_url) expect(rest).to receive(:post) reporter.run_failed(error) expect(run_data).to have_key(:error) expect(run_data[:error]).to eq "Chef::Exception::AuditError: Well that certainly didn't work\n" + "line 0\nline 1\nline 2" end end shared_context "audit data" do let(:control_group_foo) { instance_double(Chef::Audit::ControlGroupData, :metadata => double("foo metadata")) } let(:control_group_bar) { instance_double(Chef::Audit::ControlGroupData, :metadata => double("bar metadata")) } let(:ordered_control_groups) { { "foo" => control_group_foo, "bar" => control_group_bar } } let(:audit_data) { instance_double(Chef::Audit::AuditData, :add_control_group => true) } let(:run_context) { instance_double(Chef::RunContext, :audits => ordered_control_groups) } before do allow(reporter).to receive(:ordered_control_groups).and_return(ordered_control_groups) allow(reporter).to receive(:audit_data).and_return(audit_data) allow(reporter).to receive(:run_status).and_return(run_status) allow(run_status).to receive(:run_context).and_return(run_context) end end describe "#audit_phase_complete" do include_context "audit data" it "notifies audit phase finished to debug log" do expect(Chef::Log).to receive(:debug).with(/Audit Reporter completed/) reporter.audit_phase_complete end it "collects audit data" do ordered_control_groups.each do |_name, group| expect(audit_data).to receive(:add_control_group).with(group) end reporter.audit_phase_complete end end describe "#audit_phase_failed" do include_context "audit data" let(:error) { double("Exception") } it "notifies audit phase failed to debug log" do expect(Chef::Log).to receive(:debug).with(/Audit Reporter failed/) reporter.audit_phase_failed(error) end it "collects audit data" do ordered_control_groups.each do |_name, group| expect(audit_data).to receive(:add_control_group).with(group) end reporter.audit_phase_failed(error) end end describe "#control_group_started" do include_context "audit data" let(:name) { "bat" } let(:control_group) { instance_double(Chef::Audit::ControlGroupData, :metadata => double("metadata")) } before do allow(Chef::Audit::ControlGroupData).to receive(:new). with(name, control_group.metadata). and_return(control_group) end it "stores the control group" do expect(ordered_control_groups).to receive(:has_key?).with(name).and_return(false) allow(run_context.audits).to receive(:[]).with(name).and_return(control_group) expect(ordered_control_groups).to receive(:store). with(name, control_group). and_call_original reporter.control_group_started(name) # stubbed :has_key? above, which is used by the have_key matcher, # so instead we check the response to Hash's #key? because luckily # #key? does not call #has_key? expect(ordered_control_groups.key?(name)).to be true expect(ordered_control_groups[name]).to eq control_group end context "when a control group with the same name has been seen" do it "raises an exception" do expect(ordered_control_groups).to receive(:has_key?).with(name).and_return(true) expect{ reporter.control_group_started(name) }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate) end end end describe "#control_example_success" do include_context "audit data" let(:name) { "foo" } let(:example_data) { double("example data") } it "notifies the control group the example succeeded" do expect(control_group_foo).to receive(:example_success).with(example_data) reporter.control_example_success(name, example_data) end end describe "#control_example_failure" do include_context "audit data" let(:name) { "bar" } let(:example_data) { double("example data") } let(:error) { double("Exception", :message => "oopsie") } it "notifies the control group the example failed" do expect(control_group_bar).to receive(:example_failure). with(example_data, error.message) reporter.control_example_failure(name, example_data, error) end end describe "#auditing_enabled?" do shared_examples "enabled?" do |true_or_false| it "returns #{true_or_false}" do expect(Chef::Config).to receive(:[]). with(:audit_mode). and_return(audit_setting) expect(reporter.auditing_enabled?).to be true_or_false end end context "when auditing is disabled" do include_examples "enabled?", false do let(:audit_setting) { :disabled } end end context "when auditing in audit-only mode" do include_examples "enabled?", true do let(:audit_setting) { :audit_only } end end context "when auditing is enabled" do include_examples "enabled?", true do let(:audit_setting) { :enabled } end end end end chef-12.3.0/spec/unit/cookbook_version_file_specificity_spec.rb0000644000004100000410000005513312520074675024766 0ustar www-datawww-data# # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2010 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::CookbookVersion, "file specificity" do before(:each) do @cookbook = Chef::CookbookVersion.new "test-cookbook" @cookbook.manifest = { "files" => [ # afile.rb { :name => "afile.rb", :path => "files/host-examplehost.example.org/afile.rb", :checksum => "csum-host", :specificity => "host-examplehost.example.org" }, { :name => "afile.rb", :path => "files/ubuntu-9.10/afile.rb", :checksum => "csum-platver-full", :specificity => "ubuntu-9.10" }, { :name => "afile.rb", :path => "files/newubuntu-9/afile.rb", :checksum => "csum-platver-partial", :specificity => "newubuntu-9" }, { :name => "afile.rb", :path => "files/ubuntu/afile.rb", :checksum => "csum-plat", :specificity => "ubuntu" }, { :name => "afile.rb", :path => "files/default/afile.rb", :checksum => "csum-default", :specificity => "default" }, # for different/odd platform_versions { :name => "bfile.rb", :path => "files/fakeos-2.0.rc.1/bfile.rb", :checksum => "csum2-platver-full", :specificity => "fakeos-2.0.rc.1" }, { :name => "bfile.rb", :path => "files/newfakeos-2.0.rc/bfile.rb", :checksum => "csum2-platver-partial", :specificity => "newfakeos-2.0.rc" }, { :name => "bfile.rb", :path => "files/fakeos-maple tree/bfile.rb", :checksum => "csum3-platver-full", :specificity => "maple tree" }, { :name => "bfile.rb", :path => "files/fakeos-1/bfile.rb", :checksum => "csum4-platver-full", :specificity => "fakeos-1" }, # directory adirectory { :name => "anotherfile1.rb", :path => "files/host-examplehost.example.org/adirectory/anotherfile1.rb.host", :checksum => "csum-host-1", :specificity => "host-examplehost.example.org" }, { :name => "anotherfile2.rb", :path => "files/host-examplehost.example.org/adirectory/anotherfile2.rb.host", :checksum => "csum-host-2", :specificity => "host-examplehost.example.org" }, { :name => "anotherfile1.rb", :path => "files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version", :checksum => "csum-platver-full-1", :specificity => "ubuntu-9.10" }, { :name => "anotherfile2.rb", :path => "files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version", :checksum => "csum-platver-full-2", :specificity => "ubuntu-9.10" }, { :name => "anotherfile1.rb", :path => "files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version", :checksum => "csum-platver-partial-1", :specificity => "newubuntu-9" }, { :name => "anotherfile2.rb", :path => "files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version", :checksum => "csum-platver-partial-2", :specificity => "nweubuntu-9" }, { :name => "anotherfile1.rb", :path => "files/ubuntu/adirectory/anotherfile1.rb.platform", :checksum => "csum-plat-1", :specificity => "ubuntu" }, { :name => "anotherfile2.rb", :path => "files/ubuntu/adirectory/anotherfile2.rb.platform", :checksum => "csum-plat-2", :specificity => "ubuntu" }, { :name => "anotherfile1.rb", :path => "files/default/adirectory/anotherfile1.rb.default", :checksum => "csum-default-1", :specificity => "default" }, { :name => "anotherfile2.rb", :path => "files/default/adirectory/anotherfile2.rb.default", :checksum => "csum-default-2", :specificity => "default" }, # for different/odd platform_versions { :name => "anotherfile1.rb", :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version", :checksum => "csum2-platver-full-1", :specificity => "fakeos-2.0.rc.1" }, { :name => "anotherfile2.rb", :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version", :checksum => "csum2-platver-full-2", :specificity => "fakeos-2.0.rc.1" }, { :name => "anotherfile1.rb", :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version", :checksum => "csum2-platver-partial-1", :specificity => "newfakeos-2.0.rc" }, { :name => "anotherfile2.rb", :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version", :checksum => "csum2-platver-partial-2", :specificity => "newfakeos-2.0.rc" }, { :name => "anotherfile1.rb", :path => "files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version", :checksum => "csum3-platver-full-1", :specificity => "fakeos-maple tree" }, { :name => "anotherfile2.rb", :path => "files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version", :checksum => "csum3-platver-full-2", :specificity => "fakeos-maple tree" }, { :name => "anotherfile1.rb", :path => "files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version", :checksum => "csum4-platver-full-1", :specificity => "fakeos-1" }, { :name => "anotherfile2.rb", :path => "files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version", :checksum => "csum4-platver-full-2", :specificity => "fakeos-1" }, ] } end it "should return a manifest record based on priority preference: host" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "examplehost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum-host") end it "should return a manifest record based on priority preference: platform & full version" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum-platver-full") end it "should return a manifest record based on priority preference: platform & partial version" do node = Chef::Node.new node.automatic_attrs[:platform] = "newubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum-platver-partial") end it "should return a manifest record based on priority preference: platform only" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum-plat") end it "should return a manifest record based on priority preference: default" do node = Chef::Node.new node.automatic_attrs[:platform] = "notubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum-default") end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum2-platver-full") end it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "newfakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum2-platver-partial") end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "maple tree" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum3-platver-full") end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb") expect(manifest_record).not_to be_nil expect(manifest_record[:checksum]).to eq("csum4-platver-full") end describe "when fetching the contents of a directory by file specificity" do it "should return a directory of manifest records based on priority preference: host" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "examplehost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum-host-1", "csum-host-2"]) end it "should return a directory of manifest records based on priority preference: platform & full version" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum-platver-full-1", "csum-platver-full-2"]) end it "should return a directory of manifest records based on priority preference: platform & partial version" do node = Chef::Node.new node.automatic_attrs[:platform] = "newubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum-platver-partial-1", "csum-platver-partial-2"]) end it "should return a directory of manifest records based on priority preference: platform only" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum-plat-1", "csum-plat-2"]) end it "should return a directory of manifest records based on priority preference: default" do node = Chef::Node.new node.automatic_attrs[:platform] = "notubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum-default-1", "csum-default-2"]) end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum2-platver-full-1", "csum2-platver-full-2"]) end it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "newfakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum2-platver-partial-1", "csum2-platver-partial-2"]) end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "maple tree" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum3-platver-full-1", "csum3-platver-full-2"]) end it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "1" node.automatic_attrs[:fqdn] = "differenthost.example.org" manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory") expect(manifest_records).not_to be_nil expect(manifest_records.size).to eq(2) checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] } expect(checksums.sort).to eq(["csum4-platver-full-1", "csum4-platver-full-2"]) end end ## Globbing the relative paths out of the manifest records ## describe "when globbing for relative file paths based on filespecificity" do it "should return a list of relative paths based on priority preference: host" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "examplehost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.host', 'anotherfile2.rb.host']) end it "should return a list of relative paths based on priority preference: platform & full version" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version']) end it "should return a list of relative paths based on priority preference: platform & partial version" do node = Chef::Node.new node.automatic_attrs[:platform] = "newubuntu" node.automatic_attrs[:platform_version] = "9.10" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version']) end it "should return a list of relative paths based on priority preference: platform only" do node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform', 'anotherfile2.rb.platform']) end it "should return a list of relative paths based on priority preference: default" do node = Chef::Node.new node.automatic_attrs[:platform] = "notubuntu" node.automatic_attrs[:platform_version] = "1.0" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.default', 'anotherfile2.rb.default']) end it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version']) end it "should return a list of relative paths based on priority preference: platform & partial version - platform_version variant 1" do node = Chef::Node.new node.automatic_attrs[:platform] = "newfakeos" node.automatic_attrs[:platform_version] = "2.0.rc.1" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version']) end it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 2" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "maple tree" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version']) end it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 3" do node = Chef::Node.new node.automatic_attrs[:platform] = "fakeos" node.automatic_attrs[:platform_version] = "1" node.automatic_attrs[:fqdn] = "differenthost.example.org" filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory") expect(filenames).not_to be_nil expect(filenames.size).to eq(2) expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version']) end end end chef-12.3.0/spec/unit/role_spec.rb0000644000004100000410000003553612520074675016767 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/role' describe Chef::Role do before(:each) do allow(Chef::Platform).to receive(:windows?) { false } @role = Chef::Role.new @role.name("ops_master") end it "has a name" do expect(@role.name("ops_master")).to eq("ops_master") end it "does not accept a name with spaces" do expect { @role.name "ops master" }.to raise_error(ArgumentError) end it "does not accept non-String objects for the name" do expect { @role.name({}) }.to raise_error(ArgumentError) end describe "when a run list is set" do before do @role.run_list(%w{ nginx recipe[ree] role[base]}) end it "returns the run list" do expect(@role.run_list).to eq(%w{ nginx recipe[ree] role[base]}) end describe "and per-environment run lists are set" do before do @role.name("base") @role.run_list(%w{ recipe[nagios::client] recipe[tims-acl::bork]}) @role.env_run_list["prod"] = Chef::RunList.new(*(@role.run_list.to_a << "recipe[prod-base]")) @role.env_run_list["dev"] = Chef::RunList.new end it "uses the default run list as *the* run_list" do expect(@role.run_list).to eq(Chef::RunList.new("recipe[nagios::client]", "recipe[tims-acl::bork]")) end it "gives the default run list as the when getting the _default run list" do expect(@role.run_list_for("_default")).to eq(@role.run_list) end it "gives an environment specific run list" do expect(@role.run_list_for("prod")).to eq(Chef::RunList.new("recipe[nagios::client]", "recipe[tims-acl::bork]", "recipe[prod-base]")) end it "gives the default run list when no run list exists for the given environment" do expect(@role.run_list_for("qa")).to eq(@role.run_list) end it "gives the environment specific run list even if it is empty" do expect(@role.run_list_for("dev")).to eq(Chef::RunList.new) end it "env_run_lists can only be set with _default run list in it" do long_exception_name = Chef::Exceptions::InvalidEnvironmentRunListSpecification expect {@role.env_run_lists({})}.to raise_error(long_exception_name) end end describe "using the old #recipes API" do it "should let you set the recipe array" do expect(@role.recipes([ "one", "two" ])).to eq([ "one", "two" ]) end it "should let you return the recipe array" do @role.recipes([ "one", "two" ]) expect(@role.recipes).to eq([ "one", "two" ]) end it "should not list roles in the recipe array" do @role.run_list([ "one", "role[two]"]) expect(@role.recipes).to eq([ "recipe[one]", "role[two]" ]) end end end describe "default_attributes" do it "should let you set the default attributes hash explicitly" do expect(@role.default_attributes({ :one => 'two' })).to eq({ :one => 'two' }) end it "should let you return the default attributes hash" do @role.default_attributes({ :one => 'two' }) expect(@role.default_attributes).to eq({ :one => 'two' }) end it "should throw an ArgumentError if we aren't a kind of hash" do expect { @role.default_attributes(Array.new) }.to raise_error(ArgumentError) end end describe "override_attributes" do it "should let you set the override attributes hash explicitly" do expect(@role.override_attributes({ :one => 'two' })).to eq({ :one => 'two' }) end it "should let you return the override attributes hash" do @role.override_attributes({ :one => 'two' }) expect(@role.override_attributes).to eq({ :one => 'two' }) end it "should throw an ArgumentError if we aren't a kind of hash" do expect { @role.override_attributes(Array.new) }.to raise_error(ArgumentError) end end describe "update_from!" do before(:each) do @role.name('mars_volta') @role.description('Great band!') @role.run_list('one', 'two', 'role[a]') @role.default_attributes({ :el_groupo => 'nuevo' }) @role.override_attributes({ :deloused => 'in the comatorium' }) @example = Chef::Role.new @example.name('newname') @example.description('Really Great band!') @example.run_list('alpha', 'bravo', 'role[alpha]') @example.default_attributes({ :el_groupo => 'nuevo dos' }) @example.override_attributes({ :deloused => 'in the comatorium XOXO' }) end it "should update all fields except for name" do @role.update_from!(@example) expect(@role.name).to eq("mars_volta") expect(@role.description).to eq(@example.description) expect(@role.run_list).to eq(@example.run_list) expect(@role.default_attributes).to eq(@example.default_attributes) expect(@role.override_attributes).to eq(@example.override_attributes) end end describe "when serialized as JSON", :json => true do before(:each) do @role.name('mars_volta') @role.description('Great band!') @role.run_list('one', 'two', 'role[a]') @role.default_attributes({ :el_groupo => 'nuevo' }) @role.override_attributes({ :deloused => 'in the comatorium' }) @serialized_role = Chef::JSONCompat.to_json(@role) end it "should serialize to a json hash" do expect(Chef::JSONCompat.to_json(@role)).to match(/^\{.+\}$/) end it "includes the name in the JSON output" do expect(@serialized_role).to match(/"name":"mars_volta"/) end it "includes its description in the JSON" do expect(@serialized_role).to match(/"description":"Great band!"/) end it "should include 'default_attributes'" do expect(@serialized_role).to match(/"default_attributes":\{"el_groupo":"nuevo"\}/) end it "should include 'override_attributes'" do expect(@serialized_role).to match(/"override_attributes":\{"deloused":"in the comatorium"\}/) end it "should include 'run_list'" do #Activesupport messes with Chef json formatting #This test should pass with and without activesupport expect(@serialized_role).to match(/"run_list":\["recipe\[one\]","recipe\[two\]","role\[a\]"\]/) end describe "and it has per-environment run lists" do before do @role.env_run_lists("_default" => ['one', 'two', 'role[a]'], "production" => ['role[monitoring]', 'role[auditing]', 'role[apache]'], "dev" => ["role[nginx]"]) @serialized_role = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@role), :create_additions => false) end it "includes the per-environment run lists" do #Activesupport messes with Chef json formatting #This test should pass with and without activesupport expect(@serialized_role["env_run_lists"]["production"]).to eq(['role[monitoring]', 'role[auditing]', 'role[apache]']) expect(@serialized_role["env_run_lists"]["dev"]).to eq(["role[nginx]"]) end it "does not include the default environment in the per-environment run lists" do expect(@serialized_role["env_run_lists"]).not_to have_key("_default") end end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { @role } end end describe "when created from JSON", :json => true do before(:each) do @role.name('mars_volta') @role.description('Great band!') @role.run_list('one', 'two', 'role[a]') @role.default_attributes({ 'el_groupo' => 'nuevo' }) @role.override_attributes({ 'deloused' => 'in the comatorium' }) @deserial = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@role)) end it "should deserialize to a Chef::Role object" do expect(@deserial).to be_a_kind_of(Chef::Role) end %w{ name description default_attributes override_attributes run_list }.each do |t| it "should preserves the '#{t}' attribute from the JSON object" do expect(@deserial.send(t.to_sym)).to eq(@role.send(t.to_sym)) end end end ROLE_DSL=<<-EOR name "ceiling_cat" description "like Aliens, but furry" EOR describe "when loading from disk" do before do default_cache_path = windows? ? 'C:\chef' : '/var/chef' allow(Chef::Config).to receive(:cache_path).and_return(default_cache_path) end it "should return a Chef::Role object from JSON" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json"]) file_path = File.join(Chef::Config[:role_path], 'memes/lolcat.json') expect(File).to receive(:exists?).with(file_path).exactly(1).times.and_return(true) expect(IO).to receive(:read).with(file_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }') expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should return a Chef::Role object from a Ruby DSL" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.rb"]) rb_path = File.join(Chef::Config[:role_path], 'memes/lolcat.rb') expect(File).to receive(:exists?).with(rb_path).exactly(2).times.and_return(true) expect(File).to receive(:readable?).with(rb_path).exactly(1).times.and_return(true) expect(IO).to receive(:read).with(rb_path).and_return(ROLE_DSL) expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should prefer a Chef::Role Object from JSON over one from a Ruby DSL" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes", "#{Chef::Config[:role_path]}/memes/lolcat.json", "#{Chef::Config[:role_path]}/memes/lolcat.rb"]) js_path = File.join(Chef::Config[:role_path], 'memes/lolcat.json') rb_path = File.join(Chef::Config[:role_path], 'memes/lolcat.rb') expect(File).to receive(:exists?).with(js_path).exactly(1).times.and_return(true) expect(File).not_to receive(:exists?).with(rb_path) expect(IO).to receive(:read).with(js_path).and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }') expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should raise an exception if the file does not exist" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/meme.rb"]) expect(File).not_to receive(:exists?) expect {@role.class.from_disk("lolcat")}.to raise_error(Chef::Exceptions::RoleNotFound) end it "should raise an exception if two files exist with the same name" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes/lolcat.rb", "#{Chef::Config[:role_path]}/lolcat.rb"]) expect(File).not_to receive(:exists?) expect {@role.class.from_disk("lolcat")}.to raise_error(Chef::Exceptions::DuplicateRole) end it "should not raise an exception if two files exist with a similar name" do expect(Dir).to receive(:glob).and_return(["#{Chef::Config[:role_path]}/memes/lolcat.rb", "#{Chef::Config[:role_path]}/super_lolcat.rb"]) expect(File).to receive(:exists?).with("#{Chef::Config[:role_path]}/memes/lolcat.rb").and_return(true) allow_any_instance_of(Chef::Role).to receive(:from_file).with("#{Chef::Config[:role_path]}/memes/lolcat.rb") expect{ @role.class.from_disk("lolcat") }.not_to raise_error end end describe "when loading from disk and role_path is an array" do before(:each) do Chef::Config[:role_path] = ['/path1', '/path/path2'] end it "should return a Chef::Role object from JSON" do expect(Dir).to receive(:glob).with(File.join('/path1', '**', '**')).exactly(1).times.and_return(['/path1/lolcat.json']) expect(File).to receive(:exists?).with('/path1/lolcat.json').exactly(1).times.and_return(true) expect(IO).to receive(:read).with('/path1/lolcat.json').and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }') expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should return a Chef::Role object from JSON when role is in the second path" do expect(Dir).to receive(:glob).with(File.join('/path1', '**', '**')).exactly(1).times.and_return([]) expect(Dir).to receive(:glob).with(File.join('/path/path2', '**', '**')).exactly(1).times.and_return(['/path/path2/lolcat.json']) expect(File).to receive(:exists?).with('/path/path2/lolcat.json').exactly(1).times.and_return(true) expect(IO).to receive(:read).with('/path/path2/lolcat.json').and_return('{"name": "ceiling_cat", "json_class": "Chef::Role" }') expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should return a Chef::Role object from a Ruby DSL" do expect(Dir).to receive(:glob).with(File.join('/path1', '**', '**')).exactly(1).times.and_return(['/path1/lolcat.rb']) expect(File).to receive(:exists?).with('/path1/lolcat.rb').exactly(2).times.and_return(true) expect(File).to receive(:readable?).with('/path1/lolcat.rb').and_return(true) expect(IO).to receive(:read).with('/path1/lolcat.rb').exactly(1).times.and_return(ROLE_DSL) expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should return a Chef::Role object from a Ruby DSL when role is in the second path" do expect(Dir).to receive(:glob).with(File.join('/path1', '**', '**')).exactly(1).times.and_return([]) expect(Dir).to receive(:glob).with(File.join('/path/path2', '**', '**')).exactly(1).times.and_return(['/path/path2/lolcat.rb']) expect(File).to receive(:exists?).with('/path/path2/lolcat.rb').exactly(2).times.and_return(true) expect(File).to receive(:readable?).with('/path/path2/lolcat.rb').and_return(true) expect(IO).to receive(:read).with('/path/path2/lolcat.rb').exactly(1).times.and_return(ROLE_DSL) expect(@role).to be_a_kind_of(Chef::Role) @role.class.from_disk("lolcat") end it "should raise an exception if the file does not exist" do expect(Dir).to receive(:glob).with(File.join('/path1', '**', '**')).exactly(1).times.and_return([]) expect(Dir).to receive(:glob).with(File.join('/path/path2', '**', '**')).exactly(1).times.and_return([]) expect {@role.class.from_disk("lolcat")}.to raise_error(Chef::Exceptions::RoleNotFound) end end end chef-12.3.0/spec/unit/formatters/0000755000004100000410000000000012520074675016641 5ustar www-datawww-datachef-12.3.0/spec/unit/formatters/base_spec.rb0000644000004100000410000000255612520074675021122 0ustar www-datawww-data# # Author:: Lamont Granquist () # # Copyright:: Copyright (c) 2012 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Formatters::Base do let(:out) { double("out") } let(:err) { double("err") } let(:formatter) { Chef::Formatters::Base.new(out, err) } it "starts with an indentation of zero" do expect(formatter.output.indent).to eql(0) end it "increments it to two correctly" do formatter.indent_by(2) expect(formatter.output.indent).to eql(2) end it "increments it and then decrements it corectly" do formatter.indent_by(2) formatter.indent_by(-2) expect(formatter.output.indent).to eql(0) end it "does not allow negative indentation" do formatter.indent_by(-2) expect(formatter.output.indent).to eql(0) end end chef-12.3.0/spec/unit/formatters/error_inspectors/0000755000004100000410000000000012520074675022243 5ustar www-datawww-datachef-12.3.0/spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb0000644000004100000410000000164112520074675031027 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' # spec_helper loads the shared examples already. #require 'support/shared/unit/api_error_inspector_spec' describe Chef::Formatters::ErrorInspectors::NodeLoadErrorInspector do it_behaves_like "an api error inspector" end chef-12.3.0/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb0000644000004100000410000002560112520074675030535 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' BAD_RECIPE=<<-E # # Cookbook Name:: syntax-err # Recipe:: default # # Copyright 2012, YOUR_COMPANY_NAME # # All rights reserved - Do Not Redistribute # file "/tmp/explode-me" do mode 0655 owner "root" this_is_not_a_valid_method end E describe Chef::Formatters::ErrorInspectors::CompileErrorInspector do before do @node_name = "test-node.example.com" @description = Chef::Formatters::ErrorDescription.new("Error Evaluating File:") @exception = NoMethodError.new("undefined method `this_is_not_a_valid_method' for Chef::Resource::File") @outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR) #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) end describe "when scrubbing backtraces" do it "shows backtrace lines from cookbook files" do # Error inspector originally used file_cache_path which is incorrect on # chef-solo. Using cookbook_path should do the right thing for client and # solo. allow(Chef::Config).to receive(:cookbook_path).and_return([ "/home/someuser/dev-laptop/cookbooks" ]) @trace = [ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'", "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'", "/home/someuser/.multiruby/gems/chef/lib/chef/client.rb:123:in `run'" ] @exception.set_backtrace(@trace) @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb" @inspector = described_class.new(@path, @exception) @expected_filtered_trace = [ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'", "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'", ] expect(@inspector.filtered_bt).to eq(@expected_filtered_trace) end end describe "when explaining an error in the compile phase" do before do allow(Chef::Config).to receive(:cookbook_path).and_return([ "/var/chef/cache/cookbooks" ]) recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" } expect(IO).to receive(:readlines).with("/var/chef/cache/cookbooks/syntax-err/recipes/default.rb").and_return(recipe_lines) @trace = [ "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'", "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'", "/usr/local/lib/ruby/gems/chef/lib/chef/client.rb:123:in `run'" # should not display ] @exception.set_backtrace(@trace) @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb" @inspector = described_class.new(@path, @exception) @inspector.add_explanation(@description) end it "finds the line number of the error from the stacktrace" do expect(@inspector.culprit_line).to eq(14) end it "prints a pretty message" do @description.display(@outputter) end end describe "when explaining an error on windows" do before do allow(Chef::Config).to receive(:cookbook_path).and_return([ "C:/opscode/chef/var/cache/cookbooks" ]) recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" } expect(IO).to receive(:readlines).at_least(1).times.with(/:\/opscode\/chef\/var\/cache\/cookbooks\/foo\/recipes\/default.rb/).and_return(recipe_lines) @trace = [ "C:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb:14 in `from_file'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:144:in `rescue in block in load_libraries'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:138:in `block in load_libraries'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `call'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `block (2 levels) in foreach_cookbook_load_segment'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `each'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `block in foreach_cookbook_load_segment'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `each'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `foreach_cookbook_load_segment'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:137:in `load_libraries'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:62:in `load'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:198:in `setup_run_context'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:418:in `do_run'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:176:in `run'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:283:in `block in run_application'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `loop'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `run_application'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application.rb:70:in `run'", "C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/bin/chef-client:26:in `'", "C:/opscode/chef/bin/chef-client:19:in `load'", "C:/opscode/chef/bin/chef-client:19:in `
'" ] @exception.set_backtrace(@trace) @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb" @inspector = described_class.new(@path, @exception) @inspector.add_explanation(@description) end describe "and examining the stack trace for a recipe" do it "find the culprit recipe name when the drive letter is upper case" do expect(@inspector.culprit_file).to eq("C:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb") end it "find the culprit recipe name when the drive letter is lower case" do @trace.each { |line| line.gsub!(/^C:/, "c:") } @exception.set_backtrace(@trace) @inspector = described_class.new(@path, @exception) @inspector.add_explanation(@description) expect(@inspector.culprit_file).to eq("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb") end end it "finds the line number of the error from the stack trace" do expect(@inspector.culprit_line).to eq(14) end it "prints a pretty message" do @description.display(@outputter) end end describe "when explaining an error on windows, and the backtrace lowercases the drive letter" do before do allow(Chef::Config).to receive(:cookbook_path).and_return([ "C:/opscode/chef/var/cache/cookbooks" ]) recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" } expect(IO).to receive(:readlines).with("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb").and_return(recipe_lines) @trace = [ "c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb:14 in `from_file'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:144:in `rescue in block in load_libraries'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:138:in `block in load_libraries'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `call'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `block (2 levels) in foreach_cookbook_load_segment'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `each'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `block in foreach_cookbook_load_segment'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `each'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `foreach_cookbook_load_segment'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:137:in `load_libraries'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:62:in `load'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:198:in `setup_run_context'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:418:in `do_run'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:176:in `run'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:283:in `block in run_application'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `loop'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `run_application'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application.rb:70:in `run'", "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/bin/chef-client:26:in `'", "c:/opscode/chef/bin/chef-client:19:in `load'", "c:/opscode/chef/bin/chef-client:19:in `
'" ] @exception.set_backtrace(@trace) @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb" @inspector = described_class.new(@path, @exception) @inspector.add_explanation(@description) end it "finds the culprit recipe name from the stacktrace" do expect(@inspector.culprit_file).to eq("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb") end it "finds the line number of the error from the stack trace" do expect(@inspector.culprit_line).to eq(14) end it "prints a pretty message" do @description.display(@outputter) end end end chef-12.3.0/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb0000644000004100000410000001276712520074675032463 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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::Formatters::ErrorInspectors::CookbookResolveErrorInspector do before do @expanded_run_list = Chef::RunList.new("recipe[annoyances]", "recipe[apache2]", "recipe[users]", "recipe[chef::client]") @description = Chef::Formatters::ErrorDescription.new("Error Resolving Cookbooks for Run List:") @outputter_output = StringIO.new @outputter = Chef::Formatters::IndentableOutputStream.new(@outputter_output, STDERR) # @outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) end describe "when explaining a 403 error" do before do @response_body = %Q({"error": [{"message": "gtfo"}]) @response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) forbidden", @response) @inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception) @inspector.add_explanation(@description) end it "prints a nice message" do expect { @description.display(@outputter) }.not_to raise_error end end describe "when explaining a PreconditionFailed (412) error with current error message style" do # Chef currently returns error messages with some fields as JSON strings, # which must be re-parsed to get the actual data. before do @response_body = "{\"error\":[\"{\\\"non_existent_cookbooks\\\":[\\\"apache2\\\"],\\\"cookbooks_with_no_versions\\\":[\\\"users\\\"],\\\"message\\\":\\\"Run list contains invalid items: no such cookbook nope.\\\"}\"]}" @response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) precondition failed", @response) @inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception) @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) @outputter_output.rewind observed_output = @outputter_output.read expect(observed_output).to include("apache2") expect(observed_output).to include("users") expect(observed_output).not_to include("Run list contains invalid items: no such cookbook nope.") end end describe "when explaining a PreconditionFailed (412) error with current error message style without cookbook details" do # Chef currently returns error messages with some fields as JSON strings, # which must be re-parsed to get the actual data. # In some cases the error message doesn't contain any cookbook # details. But we should still print a pretty error message. before do @response_body = "{\"error\":[{\"non_existent_cookbooks\":[],\"cookbooks_with_no_versions\":[],\"message\":\"unable to solve dependencies in alotted time.\"}]}" @response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) precondition failed", @response) @inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception) @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) @outputter_output.rewind expect(@outputter_output.read).to include("unable to solve dependencies in alotted time.") end end describe "when explaining a PreconditionFailed (412) error with single encoded JSON" do # Chef currently returns error messages with some fields as JSON strings, # which must be re-parsed to get the actual data. before do @response_body = "{\"error\":[{\"non_existent_cookbooks\":[\"apache2\"],\"cookbooks_with_no_versions\":[\"users\"],\"message\":\"Run list contains invalid items: no such cookbook nope.\"}]}" @response = Net::HTTPPreconditionFailed.new("1.1", "412", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) precondition failed", @response) @inspector = Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector.new(@expanded_run_list, @exception) @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) @outputter_output.rewind observed_output = @outputter_output.read expect(observed_output).to include("apache2") expect(observed_output).to include("users") expect(observed_output).not_to include("Run list contains invalid items: no such cookbook nope.") end end end chef-12.3.0/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb0000644000004100000410000001564012520074675031234 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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::Formatters::ErrorInspectors::ResourceFailureInspector do include Chef::DSL::Recipe def run_context node = Chef::Node.new node.automatic_attrs[:platform] = "ubuntu" node.automatic_attrs[:platform_version] = "10.04" Chef::RunContext.new(node, {}, nil) end def cookbook_name "rspec-example" end def recipe_name "rspec-example-recipe" end before do @description = Chef::Formatters::ErrorDescription.new("Error Converging Resource:") @stdout = StringIO.new @outputter = Chef::Formatters::IndentableOutputStream.new(@stdout, STDERR) #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) allow(Chef::Config).to receive(:cookbook_path).and_return([ "/var/chef/cache" ]) end describe "when explaining an error converging a resource" do before do @resource = package("non-existing-package") do only_if do true end not_if("/bin/false") action :upgrade end @trace = [ "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'", "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'", "/usr/local/lib/ruby/gems/chef/lib/chef/client.rb:123:in `run'" # should not display ] @exception = Chef::Exceptions::Package.new("No such package 'non-existing-package'") @exception.set_backtrace(@trace) @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) @inspector.add_explanation(@description) end it "filters chef core code from the backtrace" do @expected_filtered_trace = [ "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'", "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'", ] expect(@inspector.filtered_bt).to eq(@expected_filtered_trace) end it "prints a pretty message" do @description.display(@outputter) end describe "and the error is a template error" do before do @description = Chef::Formatters::ErrorDescription.new("Error Converging Resource:") @template_class = Class.new { include Chef::Mixin::Template } @template = @template_class.new @context = Chef::Mixin::Template::TemplateContext.new({}) @context[:chef] = "cool" @resource = template("/tmp/foo.txt") do mode "0644" end @error = begin @context.render_template_from_string("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno") rescue Chef::Mixin::Template::TemplateError => e e end @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @error) @inspector.add_explanation(@description) end it "includes contextual info from the template error in the output" do @description.display(@outputter) expect(@stdout.string).to include(@error.source_listing) end end describe "recipe_snippet" do before do # fake code to run through #recipe_snippet source_file = [ "if true", "var = non_existent", "end" ] allow(IO).to receive(:readlines).and_return(source_file) allow(File).to receive(:exists?).and_return(true) end it "parses a Windows path" do source_line = "C:/Users/btm/chef/chef/spec/unit/fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)" @resource.source_line = source_line @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) expect(@inspector.recipe_snippet).to match(/^# In C:\/Users\/btm/) end it "parses a unix path" do source_line = "/home/btm/src/chef/chef/spec/unit/fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)" @resource.source_line = source_line @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) expect(@inspector.recipe_snippet).to match(/^# In \/home\/btm/) end context "when the recipe file does not exist" do before do allow(File).to receive(:exists?).and_return(false) allow(IO).to receive(:readlines).and_raise(Errno::ENOENT) end it "does not try to parse a recipe in chef-shell/irb (CHEF-3411)" do @resource.source_line = "(irb#1):1:in `irb_binding'" @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) expect(@inspector.recipe_snippet).to be_nil end it "does not raise an exception trying to load a non-existent file (CHEF-3411)" do @resource.source_line = "/somewhere/in/space" @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) expect { @inspector.recipe_snippet }.not_to raise_error end end end describe "when examining a resource that confuses the parser" do before do angry_bash_recipe = File.expand_path("cookbooks/angrybash/recipes/default.rb", CHEF_SPEC_DATA) source_line = "#{angry_bash_recipe}:1:in `
'" # source_line = caller(0)[0]; @resource = bash "go off the rails" do # code <<-END # for i in localhost 127.0.0.1 #{Socket.gethostname()} # do # echo "grant all on *.* to root@'$i' identified by 'a_password'; flush privileges;" | mysql -u root -h 127.0.0.1 # done # END # end @resource = eval(IO.read(angry_bash_recipe)) @resource.source_line = source_line @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) @exception.set_backtrace(@trace) @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception) end it "does not generate an error" do expect { @inspector.add_explanation(@description) }.not_to raise_error @description.display(@outputter) end end end end chef-12.3.0/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb0000644000004100000410000000655712520074675033041 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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::Formatters::ErrorInspectors::RunListExpansionErrorInspector do before do @node = Chef::Node.new.tap do |n| n.name("unit-test.example.com") n.run_list("role[base]") end @description = Chef::Formatters::ErrorDescription.new("Error Expanding RunList:") @outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR) #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) end describe "when explaining a missing role error" do before do @run_list_expansion = Chef::RunList::RunListExpansion.new("_default", @node.run_list) @run_list_expansion.missing_roles_with_including_role << [ "role[missing-role]", "role[base]" ] @run_list_expansion.missing_roles_with_including_role << [ "role[another-missing-role]", "role[base]" ] @exception = Chef::Exceptions::MissingRole.new(@run_list_expansion) @inspector = Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector.new(@node, @exception) @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) end end describe "when explaining an HTTP 403 error" do before do @response_body = "forbidden" @response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) forbidden", @response) @inspector = Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector.new(@node, @exception) allow(@inspector).to receive(:config).and_return(:node_name => "unit-test.example.com") @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) end end describe "when explaining an HTTP 401 error" do before do @response_body = "check your key and node name" @response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) unauthorized", @response) @inspector = Chef::Formatters::ErrorInspectors::RunListExpansionErrorInspector.new(@node, @exception) allow(@inspector).to receive(:config).and_return(:node_name => "unit-test.example.com", :client_key => "/etc/chef/client.pem", :chef_server_url => "http://chef.example.com") @inspector.add_explanation(@description) end it "prints a pretty message" do @description.display(@outputter) end end end chef-12.3.0/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb0000644000004100000410000000306512520074675031747 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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::Formatters::ErrorInspectors::CookbookSyncErrorInspector do before do @description = Chef::Formatters::ErrorDescription.new("Error Expanding RunList:") @outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR) #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) end describe "when explaining a 502 error" do before do @response_body = "sad trombone orchestra" @response = Net::HTTPBadGateway.new("1.1", "502", "(response) bad gateway") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPFatalError.new("(exception) bad gateway", @response) @inspector = described_class.new({}, @exception) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end end chef-12.3.0/spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb0000644000004100000410000000164512520074675031621 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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' # spec_helper loads the shared examples already. #require 'support/shared/unit/api_error_inspector_spec' describe Chef::Formatters::ErrorInspectors::RegistrationErrorInspector do it_behaves_like "an api error inspector" end chef-12.3.0/spec/unit/cookbook_loader_spec.rb0000644000004100000410000002335512520074675021156 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::CookbookLoader do before do allow(Chef::Platform).to receive(:windows?) {false} end let(:repo_paths) do [ File.expand_path(File.join(CHEF_SPEC_DATA, "kitchen")), File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) ] end let(:cookbook_loader) { Chef::CookbookLoader.new(repo_paths) } it "checks each directory only once when loading (CHEF-3487)" do cookbook_paths = [] repo_paths.each do |repo_path| cookbook_paths |= Dir[File.join(repo_path, "*")] end cookbook_paths.delete_if { |path| File.basename(path) == "chefignore" } cookbook_paths.each do |cookbook_path| expect(Chef::Cookbook::CookbookVersionLoader).to receive(:new). with(cookbook_path, anything). once. and_call_original end cookbook_loader.load_cookbooks end context "after loading all cookbooks" do before(:each) do cookbook_loader.load_cookbooks end describe "[]" do it "should return cookbook objects with []" do expect(cookbook_loader[:openldap]).to be_a_kind_of(Chef::CookbookVersion) end it "should raise an exception if it cannot find a cookbook with []" do expect { cookbook_loader[:monkeypoop] }.to raise_error(Chef::Exceptions::CookbookNotFoundInRepo) end it "should allow you to look up available cookbooks with [] and a symbol" do expect(cookbook_loader[:openldap].name).to eql(:openldap) end it "should allow you to look up available cookbooks with [] and a string" do expect(cookbook_loader["openldap"].name).to eql(:openldap) end end describe "each" do it "should allow you to iterate over cookbooks with each" do seen = Hash.new cookbook_loader.each do |cookbook_name, cookbook| seen[cookbook_name] = true end expect(seen).to have_key("openldap") expect(seen).to have_key("apache2") end it "should iterate in alphabetical order" do seen = Array.new cookbook_loader.each do |cookbook_name, cookbook| seen << cookbook_name end expect(seen[0]).to eq("angrybash") expect(seen[1]).to eq("apache2") expect(seen[2]).to eq("borken") expect(seen[3]).to eq("ignorken") expect(seen[4]).to eq("java") expect(seen[5]).to eq("name-mismatch") expect(seen[6]).to eq("openldap") end end describe "referencing cookbook files" do it "should find all the cookbooks in the cookbook path" do cookbook_loader.load_cookbooks expect(cookbook_loader).to have_key(:openldap) expect(cookbook_loader).to have_key(:apache2) end it "should allow you to override an attribute file via cookbook_path" do expect(cookbook_loader[:openldap].attribute_filenames.detect { |f| f =~ /cookbooks\/openldap\/attributes\/default.rb/ }).not_to eql(nil) expect(cookbook_loader[:openldap].attribute_filenames.detect { |f| f =~ /kitchen\/openldap\/attributes\/default.rb/ }).to eql(nil) end it "should load different attribute files from deeper paths" do expect(cookbook_loader[:openldap].attribute_filenames.detect { |f| f =~ /kitchen\/openldap\/attributes\/robinson.rb/ }).not_to eql(nil) end it "should allow you to override a definition file via cookbook_path" do expect(cookbook_loader[:openldap].definition_filenames.detect { |f| f =~ /cookbooks\/openldap\/definitions\/client.rb/ }).not_to eql(nil) expect(cookbook_loader[:openldap].definition_filenames.detect { |f| f =~ /kitchen\/openldap\/definitions\/client.rb/ }).to eql(nil) end it "should load definition files from deeper paths" do expect(cookbook_loader[:openldap].definition_filenames.detect { |f| f =~ /kitchen\/openldap\/definitions\/drewbarrymore.rb/ }).not_to eql(nil) end it "should allow you to override a recipe file via cookbook_path" do expect(cookbook_loader[:openldap].recipe_filenames.detect { |f| f =~ /cookbooks\/openldap\/recipes\/gigantor.rb/ }).not_to eql(nil) expect(cookbook_loader[:openldap].recipe_filenames.detect { |f| f =~ /kitchen\/openldap\/recipes\/gigantor.rb/ }).to eql(nil) end it "should load recipe files from deeper paths" do expect(cookbook_loader[:openldap].recipe_filenames.detect { |f| f =~ /kitchen\/openldap\/recipes\/woot.rb/ }).not_to eql(nil) end it "should allow you to have an 'ignore' file, which skips loading files in later cookbooks" do expect(cookbook_loader[:openldap].recipe_filenames.detect { |f| f =~ /kitchen\/openldap\/recipes\/ignoreme.rb/ }).to eql(nil) end it "should find files that start with a ." do expect(cookbook_loader[:openldap].file_filenames.detect { |f| f =~ /\.dotfile$/ }).to match(/\.dotfile$/) expect(cookbook_loader[:openldap].file_filenames.detect { |f| f =~ /\.ssh\/id_rsa$/ }).to match(/\.ssh\/id_rsa$/) end it "should load the metadata for the cookbook" do expect(cookbook_loader.metadata[:openldap].name.to_s).to eq("openldap") expect(cookbook_loader.metadata[:openldap]).to be_a_kind_of(Chef::Cookbook::Metadata) end end # referencing cookbook files end # loading all cookbooks context "loading all cookbooks when one has invalid metadata" do let(:repo_paths) do [ File.join(CHEF_SPEC_DATA, "kitchen"), File.join(CHEF_SPEC_DATA, "cookbooks"), File.join(CHEF_SPEC_DATA, "invalid-metadata-chef-repo") ] end it "does not squelch the exception" do expect { cookbook_loader.load_cookbooks }.to raise_error("THIS METADATA HAS A BUG") end end describe "loading only one cookbook" do let(:openldap_cookbook) { cookbook_loader["openldap"] } let(:cookbook_as_hash) { Chef::CookbookManifest.new(openldap_cookbook).to_hash } before(:each) do cookbook_loader.load_cookbook("openldap") end it "should have loaded the correct cookbook" do seen = Hash.new cookbook_loader.each do |cookbook_name, cookbook| seen[cookbook_name] = true end expect(seen).to have_key("openldap") end it "should not duplicate keys when serialized to JSON" do # Chef JSON serialization will generate duplicate keys if given # a Hash containing matching string and symbol keys. See CHEF-4571. expect(cookbook_as_hash["metadata"].recipes.keys).not_to include(:openldap) expect(cookbook_as_hash["metadata"].recipes.keys).to include("openldap") expected_desc = "Main Open LDAP configuration" expect(cookbook_as_hash["metadata"].recipes["openldap"]).to eq(expected_desc) raw = Chef::JSONCompat.to_json(cookbook_as_hash["metadata"].recipes) search_str = "\"openldap\":\"" key_idx = raw.index(search_str) expect(key_idx).to be > 0 dup_idx = raw[(key_idx + 1)..-1].index(search_str) expect(dup_idx).to be_nil end it "should not load the cookbook again when accessed" do expect(cookbook_loader).not_to receive('load_cookbook') cookbook_loader["openldap"] end it "should not load the other cookbooks" do seen = Hash.new cookbook_loader.each do |cookbook_name, cookbook| seen[cookbook_name] = true end expect(seen).not_to have_key("apache2") end it "should load another cookbook lazily with []" do expect(cookbook_loader["apache2"]).to be_a_kind_of(Chef::CookbookVersion) end context "when an unrelated cookbook has invalid metadata" do let(:repo_paths) do [ File.join(CHEF_SPEC_DATA, "kitchen"), File.join(CHEF_SPEC_DATA, "cookbooks"), File.join(CHEF_SPEC_DATA, "invalid-metadata-chef-repo") ] end it "ignores the invalid cookbook" do expect { cookbook_loader["openldap"] }.to_not raise_error end it "surfaces the exception if the cookbook is loaded later" do expect { cookbook_loader["invalid-metadata"] }.to raise_error("THIS METADATA HAS A BUG") end end describe "loading all cookbooks after loading only one cookbook" do before(:each) do cookbook_loader.load_cookbooks end it "should load all cookbooks" do seen = Hash.new cookbook_loader.each do |cookbook_name, cookbook| seen[cookbook_name] = true end expect(seen).to have_key("openldap") expect(seen).to have_key("apache2") end end end # loading only one cookbook describe "loading a single cookbook with a different name than basename" do before(:each) do cookbook_loader.load_cookbook("name-mismatch") end it "loads the correct cookbook" do cookbook_version = cookbook_loader["name-mismatch"] expect(cookbook_version).to be_a_kind_of(Chef::CookbookVersion) expect(cookbook_version.name).to eq(:"name-mismatch") end end end chef-12.3.0/spec/unit/exceptions_spec.rb0000644000004100000410000001244712520074675020203 0ustar www-datawww-data# # Author:: Thomas Bishop () # Author:: Christopher Walters () # Author:: Kyle Goodwin () # Copyright:: Copyright (c) 2010 Thomas Bishop # Copyright:: Copyright (c) 2010 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::Exceptions do exception_to_super_class = { Chef::Exceptions::Application => RuntimeError, Chef::Exceptions::Cron => RuntimeError, Chef::Exceptions::Env => RuntimeError, Chef::Exceptions::Exec => RuntimeError, Chef::Exceptions::FileNotFound => RuntimeError, Chef::Exceptions::Package => RuntimeError, Chef::Exceptions::Service => RuntimeError, Chef::Exceptions::Route => RuntimeError, Chef::Exceptions::SearchIndex => RuntimeError, Chef::Exceptions::Override => RuntimeError, Chef::Exceptions::UnsupportedAction => RuntimeError, Chef::Exceptions::MissingLibrary => RuntimeError, Chef::Exceptions::MissingRole => RuntimeError, Chef::Exceptions::CannotDetermineNodeName => RuntimeError, Chef::Exceptions::User => RuntimeError, Chef::Exceptions::Group => RuntimeError, Chef::Exceptions::Link => RuntimeError, Chef::Exceptions::Mount => RuntimeError, Chef::Exceptions::PrivateKeyMissing => RuntimeError, Chef::Exceptions::CannotWritePrivateKey => RuntimeError, Chef::Exceptions::RoleNotFound => RuntimeError, Chef::Exceptions::ValidationFailed => ArgumentError, Chef::Exceptions::InvalidPrivateKey => ArgumentError, Chef::Exceptions::ConfigurationError => ArgumentError, Chef::Exceptions::RedirectLimitExceeded => RuntimeError, Chef::Exceptions::AmbiguousRunlistSpecification => ArgumentError, Chef::Exceptions::CookbookNotFound => RuntimeError, Chef::Exceptions::AttributeNotFound => RuntimeError, Chef::Exceptions::InvalidCommandOption => RuntimeError, Chef::Exceptions::CommandTimeout => RuntimeError, Mixlib::ShellOut::ShellCommandFailed => RuntimeError, Chef::Exceptions::RequestedUIDUnavailable => RuntimeError, Chef::Exceptions::InvalidHomeDirectory => ArgumentError, Chef::Exceptions::DsclCommandFailed => RuntimeError, Chef::Exceptions::UserIDNotFound => ArgumentError, Chef::Exceptions::GroupIDNotFound => ArgumentError, Chef::Exceptions::InvalidResourceReference => RuntimeError, Chef::Exceptions::ResourceNotFound => RuntimeError, Chef::Exceptions::InvalidResourceSpecification => ArgumentError, Chef::Exceptions::SolrConnectionError => RuntimeError, Chef::Exceptions::InvalidDataBagPath => ArgumentError, Chef::Exceptions::InvalidEnvironmentPath => ArgumentError, Chef::Exceptions::EnvironmentNotFound => RuntimeError, Chef::Exceptions::InvalidVersionConstraint => ArgumentError, Chef::Exceptions::IllegalVersionConstraint => NotImplementedError } exception_to_super_class.each do |exception, expected_super_class| it "should have an exception class of #{exception} which inherits from #{expected_super_class}" do expect{ raise exception }.to raise_error(expected_super_class) end if exception.methods.include?(:to_json) include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { exception } end end end describe Chef::Exceptions::RunFailedWrappingError do shared_examples "RunFailedWrappingError expectations" do it "should initialize with a default message" do expect(e.message).to eq("Found #{num_errors} errors, they are stored in the backtrace") end it "should provide a modified backtrace when requested" do e.fill_backtrace expect(e.backtrace).to eq(backtrace) end end context "initialized with nothing" do let(:e) { Chef::Exceptions::RunFailedWrappingError.new } let(:num_errors) { 0 } let(:backtrace) { [] } include_examples "RunFailedWrappingError expectations" end context "initialized with nil" do let(:e) { Chef::Exceptions::RunFailedWrappingError.new(nil, nil) } let(:num_errors) { 0 } let(:backtrace) { [] } include_examples "RunFailedWrappingError expectations" end context "initialized with 1 error and nil" do let(:e) { Chef::Exceptions::RunFailedWrappingError.new(RuntimeError.new("foo"), nil) } let(:num_errors) { 1 } let(:backtrace) { ["1) RuntimeError - foo", ""] } include_examples "RunFailedWrappingError expectations" end context "initialized with 2 errors" do let(:e) { Chef::Exceptions::RunFailedWrappingError.new(RuntimeError.new("foo"), RuntimeError.new("bar")) } let(:num_errors) { 2 } let(:backtrace) { ["1) RuntimeError - foo", "", "2) RuntimeError - bar", ""] } include_examples "RunFailedWrappingError expectations" end end end chef-12.3.0/spec/unit/knife/0000755000004100000410000000000012520074675015547 5ustar www-datawww-datachef-12.3.0/spec/unit/knife/cookbook_create_spec.rb0000644000004100000410000002704312520074675022245 0ustar www-datawww-data# # Author:: Nuo Yan () # Copyright:: Copyright (c) 2010 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 'tmpdir' describe Chef::Knife::CookbookCreate do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::CookbookCreate.new @knife.config = {} @knife.name_args = ["foobar"] @stdout = StringIO.new allow(@knife).to receive(:stdout).and_return(@stdout) end describe "run" do # Fixes CHEF-2579 it "should expand the path of the cookbook directory" do expect(File).to receive(:expand_path).with("~/tmp/monkeypants") @knife.config = {:cookbook_path => "~/tmp/monkeypants"} allow(@knife).to receive(:create_cookbook) allow(@knife).to receive(:create_readme) allow(@knife).to receive(:create_changelog) allow(@knife).to receive(:create_metadata) @knife.run end it "should create a new cookbook with default values to copyright name, email, readme format and license if those are not supplied" do @dir = Dir.tmpdir @knife.config = {:cookbook_path => @dir} expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "YOUR_COMPANY_NAME", "YOUR_EMAIL", "none", "md") @knife.run end it "should create a new cookbook with specified company name in the copyright section if one is specified" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "YOUR_EMAIL", "none", "md") @knife.run end it "should create a new cookbook with specified copyright name and email if they are specified" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") @knife.run end it "should create a new cookbook with specified copyright name and email and license information (true) if they are specified" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "apachev2" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "apachev2") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "apachev2", "md") @knife.run end it "should create a new cookbook with specified copyright name and email and license information (false) if they are specified" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => false } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") @knife.run end it "should create a new cookbook with specified copyright name and email and license information ('false' as string) if they are specified" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "false" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") @knife.run end it "should allow specifying a gpl2 license" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "gplv2" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv2") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv2", "md") @knife.run end it "should allow specifying a gplv3 license" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "gplv3" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv3") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv3", "md") @knife.run end it "should allow specifying the mit license" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "mit" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "md") @knife.run end it "should allow specifying the rdoc readme format" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "mit", :readme_format => "rdoc" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "rdoc") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "rdoc") @knife.run end it "should allow specifying the mkd readme format" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "mit", :readme_format => "mkd" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "mkd") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "mkd") @knife.run end it "should allow specifying the txt readme format" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "mit", :readme_format => "txt" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "txt") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "txt") @knife.run end it "should allow specifying an arbitrary readme format" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, :cookbook_copyright => "Opscode, Inc", :cookbook_email => "nuo@opscode.com", :cookbook_license => "mit", :readme_format => "foo" } @knife.name_args=["foobar"] expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "foo") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "foo") @knife.run end context "when the cookbooks path is set to nil" do before do Chef::Config[:cookbook_path] = nil end it "should throw an argument error" do @dir = Dir.tmpdir expect{@knife.run}.to raise_error(ArgumentError) end end end end chef-12.3.0/spec/unit/knife/node_from_file_spec.rb0000644000004100000410000000343512520074675022062 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' Chef::Knife::NodeFromFile.load_deps describe Chef::Knife::NodeFromFile do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeFromFile.new @knife.config = { :print_after => nil } @knife.name_args = [ "adam.rb" ] allow(@knife).to receive(:output).and_return(true) allow(@knife).to receive(:confirm).and_return(true) @node = Chef::Node.new() allow(@node).to receive(:save) allow(@knife.loader).to receive(:load_from).and_return(@node) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should load from a file" do expect(@knife.loader).to receive(:load_from).with('nodes', 'adam.rb').and_return(@node) @knife.run end it "should not print the Node" do expect(@knife).not_to receive(:output) @knife.run end describe "with -p or --print-after" do it "should print the Node" do @knife.config[:print_after] = true expect(@knife).to receive(:output) @knife.run end end end end chef-12.3.0/spec/unit/knife/client_delete_spec.rb0000644000004100000410000000511712520074675021712 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ClientDelete do before(:each) do @knife = Chef::Knife::ClientDelete.new # defaults @knife.config = { :delete_validators => false } @knife.name_args = [ 'adam' ] end describe 'run' do it 'should delete the client' do expect(@knife).to receive(:delete_object).with(Chef::ApiClient, 'adam', 'client') @knife.run end it 'should print usage and exit when a client name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end describe 'with a validator' do before(:each) do allow(Chef::Knife::UI).to receive(:confirm).and_return(true) allow(@knife).to receive(:confirm).and_return(true) @client = Chef::ApiClient.new expect(Chef::ApiClient).to receive(:load).and_return(@client) end it 'should delete non-validator client if --delete-validators is not set' do @knife.config[:delete_validators] = false expect(@client).to receive(:destroy).and_return(@client) expect(@knife).to receive(:msg) @knife.run end it 'should delete non-validator client if --delete-validators is set' do @knife.config[:delete_validators] = true expect(@client).to receive(:destroy).and_return(@client) expect(@knife).to receive(:msg) @knife.run end it 'should not delete validator client if --delete-validators is not set' do @client.validator(true) expect(@knife.ui).to receive(:fatal) expect { @knife.run}.to raise_error(SystemExit) end it 'should delete validator client if --delete-validators is set' do @knife.config[:delete_validators] = true expect(@client).to receive(:destroy).and_return(@client) expect(@knife).to receive(:msg) @knife.run end end end chef-12.3.0/spec/unit/knife/environment_delete_spec.rb0000644000004100000410000000435512520074675023003 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::EnvironmentDelete do before(:each) do @knife = Chef::Knife::EnvironmentDelete.new allow(@knife).to receive(:msg).and_return true allow(@knife).to receive(:output).and_return true allow(@knife).to receive(:show_usage).and_return true allow(@knife).to receive(:confirm).and_return true @knife.name_args = [ "production" ] @environment = Chef::Environment.new @environment.name("production") @environment.description("Please delete me") allow(@environment).to receive(:destroy).and_return true allow(Chef::Environment).to receive(:load).and_return @environment end it "should confirm that you want to delete" do expect(@knife).to receive(:confirm) @knife.run end it "should load the environment" do expect(Chef::Environment).to receive(:load).with("production") @knife.run end it "should delete the environment" do expect(@environment).to receive(:destroy) @knife.run end it "should not print the environment" do expect(@knife).not_to receive(:output) @knife.run end it "should show usage and exit when no environment name is provided" do @knife.name_args = [] expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end describe "with --print-after" do it "should pretty print the environment, formatted for display" do @knife.config[:print_after] = true expect(@knife).to receive(:output).with(@environment) @knife.run end end end chef-12.3.0/spec/unit/knife/cookbook_show_spec.rb0000644000004100000410000002107312520074675021757 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, eersion 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. # # rename to cookbook not coookbook require 'spec_helper' describe Chef::Knife::CookbookShow do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::CookbookShow.new @knife.config = { } @knife.name_args = [ "cookbook_name" ] @rest = double(Chef::REST) allow(@knife).to receive(:rest).and_return(@rest) allow(@knife).to receive(:pretty_print).and_return(true) allow(@knife).to receive(:output).and_return(true) end describe "run" do describe "with 0 arguments: help" do it 'should should print usage and exit when given no arguments' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end describe "with 1 argument: versions" do before(:each) do @response = { "cookbook_name" => { "url" => "http://url/cookbooks/cookbook_name", "versions" => [ { "version" => "0.10.0", "url" => "http://url/cookbooks/cookbook_name/0.10.0" }, { "version" => "0.9.0", "url" => "http://url/cookbookx/cookbook_name/0.9.0" }, { "version" => "0.8.0", "url" => "http://url/cookbooks/cookbook_name/0.8.0" } ] } } end it "should show the raw cookbook data" do expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name").and_return(@response) expect(@knife).to receive(:format_cookbook_list_for_display).with(@response) @knife.run end it "should respect the user-supplied environment" do @knife.config[:environment] = "foo" expect(@rest).to receive(:get_rest).with("environments/foo/cookbooks/cookbook_name").and_return(@response) expect(@knife).to receive(:format_cookbook_list_for_display).with(@response) @knife.run end end describe "with 2 arguments: name and version" do before(:each) do @knife.name_args << "0.1.0" @response = { "0.1.0" => { "recipes" => {"default.rb" => ""} } } end it "should show the specific part of a cookbook" do expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@response) expect(@knife).to receive(:output).with(@response) @knife.run end end describe "with 3 arguments: name, version, and segment" do before(:each) do @knife.name_args = [ "cookbook_name", "0.1.0", "recipes" ] @cookbook_response = Chef::CookbookVersion.new("cookbook_name") @manifest = { "recipes" => [ { :name => "default.rb", :path => "recipes/default.rb", :checksum => "1234", :url => "http://example.org/files/default.rb" } ] } @cookbook_response.manifest = @manifest @response = {"name"=>"default.rb", "url"=>"http://example.org/files/default.rb", "checksum"=>"1234", "path"=>"recipes/default.rb"} end it "should print the json of the part" do expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@knife).to receive(:output).with(@cookbook_response.manifest["recipes"]) @knife.run end end describe "with 4 arguments: name, version, segment and filename" do before(:each) do @knife.name_args = [ "cookbook_name", "0.1.0", "recipes", "default.rb" ] @cookbook_response = Chef::CookbookVersion.new("cookbook_name") @cookbook_response.manifest = { "recipes" => [ { :name => "default.rb", :path => "recipes/default.rb", :checksum => "1234", :url => "http://example.org/files/default.rb" } ] } @response = "Example recipe text" end it "should print the raw result of the request (likely a file!)" do expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@rest).to receive(:get_rest).with("http://example.org/files/default.rb", true).and_return(StringIO.new(@response)) expect(@knife).to receive(:pretty_print).with(@response) @knife.run end end describe "with 4 arguments: name, version, segment and filename -- with specificity" do before(:each) do @knife.name_args = [ "cookbook_name", "0.1.0", "files", "afile.rb" ] @cookbook_response = Chef::CookbookVersion.new("cookbook_name") @cookbook_response.manifest = { "files" => [ { :name => "afile.rb", :path => "files/host-examplehost.example.org/afile.rb", :checksum => "1111", :specificity => "host-examplehost.example.org", :url => "http://example.org/files/1111" }, { :name => "afile.rb", :path => "files/ubuntu-9.10/afile.rb", :checksum => "2222", :specificity => "ubuntu-9.10", :url => "http://example.org/files/2222" }, { :name => "afile.rb", :path => "files/ubuntu/afile.rb", :checksum => "3333", :specificity => "ubuntu", :url => "http://example.org/files/3333" }, { :name => "afile.rb", :path => "files/default/afile.rb", :checksum => "4444", :specificity => "default", :url => "http://example.org/files/4444" }, ] } @response = "Example recipe text" end describe "with --fqdn" do it "should pass the fqdn" do @knife.config[:platform] = "example_platform" @knife.config[:platform_version] = "1.0" @knife.config[:fqdn] = "examplehost.example.org" expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@rest).to receive(:get_rest).with("http://example.org/files/1111", true).and_return(StringIO.new(@response)) expect(@knife).to receive(:pretty_print).with(@response) @knife.run end end describe "and --platform" do it "should pass the platform" do @knife.config[:platform] = "ubuntu" @knife.config[:platform_version] = "1.0" @knife.config[:fqdn] = "differenthost.example.org" expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@rest).to receive(:get_rest).with("http://example.org/files/3333", true).and_return(StringIO.new(@response)) expect(@knife).to receive(:pretty_print).with(@response) @knife.run end end describe "and --platform-version" do it "should pass the platform" do @knife.config[:platform] = "ubuntu" @knife.config[:platform_version] = "9.10" @knife.config[:fqdn] = "differenthost.example.org" expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@rest).to receive(:get_rest).with("http://example.org/files/2222", true).and_return(StringIO.new(@response)) expect(@knife).to receive(:pretty_print).with(@response) @knife.run end end describe "with none of the arguments, it should use the default" do it "should pass them all" do expect(@rest).to receive(:get_rest).with("cookbooks/cookbook_name/0.1.0").and_return(@cookbook_response) expect(@rest).to receive(:get_rest).with("http://example.org/files/4444", true).and_return(StringIO.new(@response)) expect(@knife).to receive(:pretty_print).with(@response) @knife.run end end end end end chef-12.3.0/spec/unit/knife/node_list_spec.rb0000644000004100000410000000412512520074675021070 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeList do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" Chef::Config[:environment] = nil # reset this value each time, as it is not reloaded @knife = Chef::Knife::NodeList.new allow(@knife).to receive(:output).and_return(true) @list = { "foo" => "http://example.com/foo", "bar" => "http://example.com/foo" } allow(Chef::Node).to receive(:list).and_return(@list) allow(Chef::Node).to receive(:list_by_environment).and_return(@list) end describe "run" do it "should list all of the nodes if -E is not specified" do expect(Chef::Node).to receive(:list).and_return(@list) @knife.run end it "should pretty print the list" do expect(Chef::Node).to receive(:list).and_return(@list) expect(@knife).to receive(:output).with([ "bar", "foo" ]) @knife.run end it "should list nodes in the specific environment if -E ENVIRONMENT is specified" do Chef::Config[:environment] = "prod" expect(Chef::Node).to receive(:list_by_environment).with("prod").and_return(@list) @knife.run end describe "with -w or --with-uri" do it "should pretty print the hash" do @knife.config[:with_uri] = true expect(Chef::Node).to receive(:list).and_return(@list) expect(@knife).to receive(:output).with(@list) @knife.run end end end end chef-12.3.0/spec/unit/knife/data_bag_show_spec.rb0000644000004100000410000001100312520074675021663 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2008-2010 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/data_bag_item' require 'chef/encrypted_data_bag_item' require 'chef/json_compat' require 'tempfile' describe Chef::Knife::DataBagShow do before do Chef::Config[:node_name] = "webmonkey.example.com" knife.name_args = [bag_name, item_name] allow(knife).to receive(:config).and_return(config) end let(:knife) do k = Chef::Knife::DataBagShow.new allow(k).to receive(:rest).and_return(rest) allow(k.ui).to receive(:stdout).and_return(stdout) k end let(:rest) { double("Chef::REST") } let(:stdout) { StringIO.new } let(:bag_name) { "sudoing_admins" } let(:item_name) { "ME" } let(:data_bag_contents) { { "id" => "id", "baz"=>"http://localhost:4000/data/bag_o_data/baz", "qux"=>"http://localhost:4000/data/bag_o_data/qux"} } let(:enc_hash) {Chef::EncryptedDataBagItem.encrypt_data_bag_item(data_bag_contents, secret)} let(:data_bag) {Chef::DataBagItem.from_hash(data_bag_contents)} let(:data_bag_with_encoded_hash) { Chef::DataBagItem.from_hash(enc_hash) } let(:enc_data_bag) { Chef::EncryptedDataBagItem.new(enc_hash, secret) } let(:secret) { "abc123SECRET" } # # let(:raw_hash) {{ "login_name" => "alphaomega", "id" => item_name }} # let(:config) { {format: "json"} } context "Data bag to show is encrypted" do before do allow(knife).to receive(:encrypted?).and_return(true) end it "decrypts and displays the encrypted data bag when the secret is provided" do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true) expect(knife).to receive(:read_secret).and_return(secret) expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(data_bag_with_encoded_hash) expect(knife.ui).to receive(:info).with("Encrypted data bag detected, decrypting with provided secret.") expect(Chef::EncryptedDataBagItem).to receive(:load).with(bag_name, item_name, secret).and_return(enc_data_bag) expected = %q|baz: http://localhost:4000/data/bag_o_data/baz id: id qux: http://localhost:4000/data/bag_o_data/qux| knife.run expect(stdout.string.strip).to eq(expected) end it "displays the encrypted data bag when the secret is not provided" do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false) expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(data_bag_with_encoded_hash) expect(knife.ui).to receive(:warn).with("Encrypted data bag detected, but no secret provided for decoding. Displaying encrypted data.") knife.run expect(stdout.string.strip).to include("baz", "qux", "cipher") end end context "Data bag to show is not encrypted" do before do allow(knife).to receive(:encrypted?).and_return(false) expect(knife).to receive(:read_secret).exactly(0).times end it "displays the data bag" do expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(data_bag) expect(knife.ui).to receive(:info).with("Unencrypted data bag detected, ignoring any provided secret options.") expected = %q|baz: http://localhost:4000/data/bag_o_data/baz id: id qux: http://localhost:4000/data/bag_o_data/qux| knife.run expect(stdout.string.strip).to eq(expected) end end it "displays the list of items in the data bag when only one @name_arg is provided" do knife.name_args = [bag_name] expect(Chef::DataBag).to receive(:load).with(bag_name).and_return({}) knife.run expect(stdout.string.strip).to eq("") end it "raises an error when no @name_args are provided" do knife.name_args = [] expect {knife.run}.to exit_with_code(1) expect(stdout.string).to start_with("knife data bag show BAG [ITEM] (options)") end end chef-12.3.0/spec/unit/knife/role_from_file_spec.rb0000644000004100000410000000417412520074675022077 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' Chef::Knife::RoleFromFile.load_deps describe Chef::Knife::RoleFromFile do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleFromFile.new @knife.config = { :print_after => nil } @knife.name_args = [ "adam.rb" ] allow(@knife).to receive(:output).and_return(true) allow(@knife).to receive(:confirm).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:save) allow(@knife.loader).to receive(:load_from).and_return(@role) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should load from a file" do expect(@knife.loader).to receive(:load_from).with('roles', 'adam.rb').and_return(@role) @knife.run end it "should not print the role" do expect(@knife).not_to receive(:output) @knife.run end describe "with -p or --print-after" do it "should print the role" do @knife.config[:print_after] = true expect(@knife).to receive(:output) @knife.run end end end describe "run with multiple arguments" do it "should load each file" do @knife.name_args = [ "adam.rb", "caleb.rb" ] expect(@knife.loader).to receive(:load_from).with('roles', 'adam.rb').and_return(@role) expect(@knife.loader).to receive(:load_from).with('roles', 'caleb.rb').and_return(@role) @knife.run end end end chef-12.3.0/spec/unit/knife/cookbook_upload_spec.rb0000644000004100000410000003075012520074675022265 0ustar www-datawww-data# # Author:: Matthew Kent () # 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) require 'chef/cookbook_uploader' require 'timeout' describe Chef::Knife::CookbookUpload do let(:cookbook) { Chef::CookbookVersion.new('test_cookbook', '/tmp/blah.txt') } let(:cookbooks_by_name) do {cookbook.name => cookbook} end let(:cookbook_loader) do cookbook_loader = cookbooks_by_name.dup allow(cookbook_loader).to receive(:merged_cookbooks).and_return([]) allow(cookbook_loader).to receive(:load_cookbooks).and_return(cookbook_loader) cookbook_loader end let(:cookbook_uploader) { double(:upload_cookbooks => nil) } let(:output) { StringIO.new } let(:name_args) { ['test_cookbook'] } let(:knife) do k = Chef::Knife::CookbookUpload.new k.name_args = name_args allow(k.ui).to receive(:stdout).and_return(output) allow(k.ui).to receive(:stderr).and_return(output) k end before(:each) do allow(Chef::CookbookLoader).to receive(:new).and_return(cookbook_loader) end describe 'with --concurrency' do it 'should upload cookbooks with predefined concurrency' do allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({}) knife.config[:concurrency] = 3 test_cookbook = Chef::CookbookVersion.new('test_cookbook', '/tmp/blah') allow(cookbook_loader).to receive(:each).and_yield("test_cookbook", test_cookbook) allow(cookbook_loader).to receive(:cookbook_names).and_return(["test_cookbook"]) expect(Chef::CookbookUploader).to receive(:new). with( kind_of(Array), { :force => nil, :concurrency => 3}). and_return(double("Chef::CookbookUploader", :upload_cookbooks=> true)) knife.run end end describe 'run' do before(:each) do allow(Chef::CookbookUploader).to receive_messages(:new => cookbook_uploader) allow(Chef::CookbookVersion).to receive(:list_all_versions).and_return({}) end it 'should print usage and exit when a cookbook name is not provided' do knife.name_args = [] expect(knife).to receive(:show_usage) expect(knife.ui).to receive(:fatal) expect { knife.run }.to raise_error(SystemExit) end describe 'when specifying a cookbook name' do it 'should upload the cookbook' do expect(knife).to receive(:upload).once knife.run end it 'should report on success' do expect(knife).to receive(:upload).once expect(knife.ui).to receive(:info).with(/Uploaded 1 cookbook/) knife.run end end describe 'when specifying the same cookbook name twice' do it 'should upload the cookbook only once' do knife.name_args = ['test_cookbook', 'test_cookbook'] expect(knife).to receive(:upload).once knife.run end end context "when uploading a cookbook that uses deprecated overlays" do before do allow(cookbook_loader).to receive(:merged_cookbooks).and_return(['test_cookbook']) allow(cookbook_loader).to receive(:merged_cookbook_paths). and_return({'test_cookbook' => %w{/path/one/test_cookbook /path/two/test_cookbook}}) end it "emits a warning" do knife.run expected_message=<<-E WARNING: The cookbooks: test_cookbook exist in multiple places in your cookbook_path. A composite version of these cookbooks has been compiled for uploading. IMPORTANT: In a future version of Chef, this behavior will be removed and you will no longer be able to have the same version of a cookbook in multiple places in your cookbook_path. WARNING: The affected cookbooks are located: test_cookbook: /path/one/test_cookbook /path/two/test_cookbook E expect(output.string).to include(expected_message) end end describe 'when specifying a cookbook name among many' do let(:name_args) { ['test_cookbook1'] } let(:cookbooks_by_name) do { 'test_cookbook1' => Chef::CookbookVersion.new('test_cookbook1', '/tmp/blah'), 'test_cookbook2' => Chef::CookbookVersion.new('test_cookbook2', '/tmp/blah'), 'test_cookbook3' => Chef::CookbookVersion.new('test_cookbook3', '/tmp/blah') } end it "should read only one cookbook" do expect(cookbook_loader).to receive(:[]).once.with('test_cookbook1').and_call_original knife.run end it "should not read all cookbooks" do expect(cookbook_loader).not_to receive(:load_cookbooks) knife.run end it "should upload only one cookbook" do expect(knife).to receive(:upload).exactly(1).times knife.run end end # This is testing too much. We should break it up. describe 'when specifying a cookbook name with dependencies' do let(:name_args) { ["test_cookbook2"] } let(:cookbooks_by_name) do { "test_cookbook1" => test_cookbook1, "test_cookbook2" => test_cookbook2, "test_cookbook3" => test_cookbook3 } end let(:test_cookbook1) { Chef::CookbookVersion.new('test_cookbook1', '/tmp/blah') } let(:test_cookbook2) do c = Chef::CookbookVersion.new('test_cookbook2') c.metadata.depends("test_cookbook3") c end let(:test_cookbook3) do c = Chef::CookbookVersion.new('test_cookbook3') c.metadata.depends("test_cookbook1") c.metadata.depends("test_cookbook2") c end it "should upload all dependencies once" do knife.config[:depends] = true allow(knife).to receive(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2", "test_cookbook3"]) expect(knife).to receive(:upload).exactly(3).times expect do Timeout::timeout(5) do knife.run end end.not_to raise_error end end describe 'when specifying a cookbook name with missing dependencies' do let(:cookbook_dependency) { Chef::CookbookVersion.new('dependency', '/tmp/blah') } before(:each) do cookbook.metadata.depends("dependency") allow(cookbook_loader).to receive(:[]) do |ckbk| { "test_cookbook" => cookbook, "dependency" => cookbook_dependency}[ckbk] end allow(knife).to receive(:cookbook_names).and_return(["cookbook_dependency", "test_cookbook"]) @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {}) end it 'should exit and not upload the cookbook' do expect(cookbook_loader).to receive(:[]).once.with('test_cookbook') expect(cookbook_loader).not_to receive(:load_cookbooks) expect(cookbook_uploader).not_to receive(:upload_cookbooks) expect {knife.run}.to raise_error(SystemExit) end it 'should output a message for a single missing dependency' do expect {knife.run}.to raise_error(SystemExit) expect(@stderr.string).to include('Cookbook test_cookbook depends on cookbooks which are not currently') expect(@stderr.string).to include('being uploaded and cannot be found on the server.') expect(@stderr.string).to include("The missing cookbook(s) are: 'dependency' version '>= 0.0.0'") end it 'should output a message for a multiple missing dependencies which are concatenated' do cookbook_dependency2 = Chef::CookbookVersion.new('dependency2') cookbook.metadata.depends("dependency2") allow(cookbook_loader).to receive(:[]) do |ckbk| { "test_cookbook" => cookbook, "dependency" => cookbook_dependency, "dependency2" => cookbook_dependency2}[ckbk] end allow(knife).to receive(:cookbook_names).and_return(["dependency", "dependency2", "test_cookbook"]) expect {knife.run}.to raise_error(SystemExit) expect(@stderr.string).to include('Cookbook test_cookbook depends on cookbooks which are not currently') expect(@stderr.string).to include('being uploaded and cannot be found on the server.') expect(@stderr.string).to include("The missing cookbook(s) are:") expect(@stderr.string).to include("'dependency' version '>= 0.0.0'") expect(@stderr.string).to include("'dependency2' version '>= 0.0.0'") end end it "should freeze the version of the cookbooks if --freeze is specified" do knife.config[:freeze] = true expect(cookbook).to receive(:freeze_version).once knife.run end describe 'with -a or --all' do before(:each) do knife.config[:all] = true end context 'when cookbooks exist in the cookbook path' do before(:each) do @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1', '/tmp/blah') @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2', '/tmp/blah') allow(cookbook_loader).to receive(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2) allow(cookbook_loader).to receive(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"]) end it 'should upload all cookbooks' do expect(knife).to receive(:upload).once knife.run end it 'should report on success' do expect(knife).to receive(:upload).once expect(knife.ui).to receive(:info).with(/Uploaded all cookbooks/) knife.run end it 'should update the version constraints for an environment' do allow(knife).to receive(:assert_environment_valid!).and_return(true) knife.config[:environment] = "production" expect(knife).to receive(:update_version_constraints).once knife.run end end context 'when no cookbooks exist in the cookbook path' do before(:each) do allow(cookbook_loader).to receive(:each) end it 'should not upload any cookbooks' do expect(knife).to_not receive(:upload) knife.run end context 'when cookbook path is an array' do it 'should warn users that no cookbooks exist' do knife.config[:cookbook_path] = ['/chef-repo/cookbooks', '/home/user/cookbooks'] expect(knife.ui).to receive(:warn).with( /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path].join(', ')}\. Use --cookbook-path to specify the desired path\./) knife.run end end context 'when cookbook path is a string' do it 'should warn users that no cookbooks exist' do knife.config[:cookbook_path] = '/chef-repo/cookbooks' expect(knife.ui).to receive(:warn).with( /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path]}\. Use --cookbook-path to specify the desired path\./) knife.run end end end end describe 'when a frozen cookbook exists on the server' do it 'should fail to replace it' do exception = Chef::Exceptions::CookbookFrozen.new expect(cookbook_uploader).to receive(:upload_cookbooks). and_raise(exception) allow(knife.ui).to receive(:error) expect(knife.ui).to receive(:error).with(exception) expect { knife.run }.to raise_error(SystemExit) end it 'should not update the version constraints for an environment' do allow(knife).to receive(:assert_environment_valid!).and_return(true) knife.config[:environment] = "production" allow(knife).to receive(:upload).and_raise(Chef::Exceptions::CookbookFrozen) expect(knife.ui).to receive(:error).with(/Failed to upload 1 cookbook/) expect(knife.ui).to receive(:warn).with(/Not updating version constraints/) expect(knife).not_to receive(:update_version_constraints) expect { knife.run }.to raise_error(SystemExit) end end end # run end chef-12.3.0/spec/unit/knife/client_list_spec.rb0000644000004100000410000000203012520074675021412 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ClientList do before(:each) do @knife = Chef::Knife::ClientList.new @knife.name_args = [ 'adam' ] end describe 'run' do it 'should list the clients' do expect(Chef::ApiClient).to receive(:list) expect(@knife).to receive(:format_list_for_display) @knife.run end end end chef-12.3.0/spec/unit/knife/cookbook_metadata_spec.rb0000644000004100000410000001746312520074675022567 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::CookbookMetadata do before(:each) do @knife = Chef::Knife::CookbookMetadata.new @knife.name_args = ['foobar'] @cookbook_dir = Dir.mktmpdir @json_data = '{ "version": "1.0.0" }' @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe 'run' do it 'should print an error and exit if a cookbook name was not provided' do @knife.name_args = [] expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i) expect { @knife.run }.to raise_error(SystemExit) end it 'should print an error and exit if an empty cookbook name was provided' do @knife.name_args = [''] expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i) expect { @knife.run }.to raise_error(SystemExit) end it 'should generate the metadata for the cookbook' do expect(@knife).to receive(:generate_metadata).with('foobar') @knife.run end describe 'with -a or --all' do before(:each) do @knife.config[:all] = true @foo = Chef::CookbookVersion.new('foo', '/tmp/blah') @foo.version = '1.0.0' @bar = Chef::CookbookVersion.new('bar', '/tmp/blah') @bar.version = '2.0.0' @cookbook_loader = { "foo" => @foo, "bar" => @bar } expect(@cookbook_loader).to receive(:load_cookbooks).and_return(@cookbook_loader) expect(@knife).to receive(:generate_metadata).with('foo') expect(@knife).to receive(:generate_metadata).with('bar') end it 'should generate the metadata for each cookbook' do Chef::Config[:cookbook_path] = @cookbook_dir expect(Chef::CookbookLoader).to receive(:new).with(@cookbook_dir).and_return(@cookbook_loader) @knife.run end describe 'and with -o or --cookbook-path' do it 'should look in the provided path and generate cookbook metadata' do @knife.config[:cookbook_path] = '/opt/chef/cookbooks' expect(Chef::CookbookLoader).to receive(:new).with('/opt/chef/cookbooks').and_return(@cookbook_loader) @knife.run end end end end describe 'generate_metadata' do before(:each) do @knife.config[:cookbook_path] = @cookbook_dir allow(File).to receive(:expand_path).with("#{@cookbook_dir}/foobar/metadata.rb"). and_return("#{@cookbook_dir}/foobar/metadata.rb") end it 'should generate the metadata from metadata.rb if it exists' do expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb"). and_return(true) expect(@knife).to receive(:generate_metadata_from_file).with('foobar', "#{@cookbook_dir}/foobar/metadata.rb") @knife.run end it 'should validate the metadata json if metadata.rb does not exist' do expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb"). and_return(false) expect(@knife).to receive(:validate_metadata_json).with(@cookbook_dir, 'foobar') @knife.run end end describe 'generate_metadata_from_file' do before(:each) do @metadata_mock = double('metadata') @json_file_mock = double('json_file') end it 'should generate the metatdata json from metatdata.rb' do allow(Chef::Cookbook::Metadata).to receive(:new).and_return(@metadata_mock) expect(@metadata_mock).to receive(:name).with('foobar') expect(@metadata_mock).to receive(:from_file).with("#{@cookbook_dir}/foobar/metadata.rb") expect(File).to receive(:open).with("#{@cookbook_dir}/foobar/metadata.json", 'w'). and_yield(@json_file_mock) expect(@json_file_mock).to receive(:write).with(@json_data) expect(Chef::JSONCompat).to receive(:to_json_pretty).with(@metadata_mock). and_return(@json_data) @knife.generate_metadata_from_file('foobar', "#{@cookbook_dir}/foobar/metadata.rb") expect(@stderr.string).to match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im end { Chef::Exceptions::ObsoleteDependencySyntax => 'obsolote dependency', Chef::Exceptions::InvalidVersionConstraint => 'invalid version constraint' }.each_pair do |klass, description| it "should print an error and exit when an #{description} syntax exception is encountered" do exception = klass.new("#{description} blah") allow(Chef::Cookbook::Metadata).to receive(:new).and_raise(exception) expect { @knife.generate_metadata_from_file('foobar', "#{@cookbook_dir}/foobar/metadata.rb") }.to raise_error(SystemExit) expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.rb/im expect(@stderr.string).to match /#{description} blah/im end end end describe 'validate_metadata_json' do it 'should validate the metadata json' do expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json"). and_return(true) expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json"). and_return(@json_data) expect(Chef::Cookbook::Metadata).to receive(:validate_json).with(@json_data) @knife.validate_metadata_json(@cookbook_dir, 'foobar') end it 'should not try to validate the metadata json if the file does not exist' do expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json"). and_return(false) expect(IO).not_to receive(:read) expect(Chef::Cookbook::Metadata).not_to receive(:validate_json) @knife.validate_metadata_json(@cookbook_dir, 'foobar') end { Chef::Exceptions::ObsoleteDependencySyntax => 'obsolote dependency', Chef::Exceptions::InvalidVersionConstraint => 'invalid version constraint' }.each_pair do |klass, description| it "should print an error and exit when an #{description} syntax exception is encountered" do expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json"). and_return(true) expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json"). and_return(@json_data) exception = klass.new("#{description} blah") allow(Chef::Cookbook::Metadata).to receive(:validate_json).and_raise(exception) expect { @knife.validate_metadata_json(@cookbook_dir, 'foobar') }.to raise_error(SystemExit) expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.json/im expect(@stderr.string).to match /#{description} blah/im end end end end chef-12.3.0/spec/unit/knife/core/0000755000004100000410000000000012520074675016477 5ustar www-datawww-datachef-12.3.0/spec/unit/knife/core/object_loader_spec.rb0000644000004100000410000000453012520074675022634 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Juanje Ojeda () # Copyright:: Copyright (c) 2011-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/knife/core/object_loader' describe Chef::Knife::Core::ObjectLoader do before(:each) do @knife = Chef::Knife.new @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) Dir.chdir(File.join(CHEF_SPEC_DATA, 'object_loader')) end shared_examples_for "Chef object" do |chef_class| it "should create a #{chef_class} object" do expect(@object).to be_a_kind_of(chef_class) end it "should has a attribute 'name'" do expect(@object.name).to eql('test') end end { 'nodes' => Chef::Node, 'roles' => Chef::Role, 'environments' => Chef::Environment }.each do |repo_location, chef_class| describe "when the file is a #{chef_class}" do before do @loader = Chef::Knife::Core::ObjectLoader.new(chef_class, @knife.ui) end describe "when the file is a Ruby" do before do @object = @loader.load_from(repo_location, 'test.rb') end it_behaves_like "Chef object", chef_class end #NOTE: This is check for the bug described at CHEF-2352 describe "when the file is a JSON" do describe "and it has defined 'json_class'" do before do @object = @loader.load_from(repo_location, 'test_json_class.json') end it_behaves_like "Chef object", chef_class end describe "and it has not defined 'json_class'" do before do @object = @loader.load_from(repo_location, 'test.json') end it_behaves_like "Chef object", chef_class end end end end end chef-12.3.0/spec/unit/knife/core/cookbook_scm_repo_spec.rb0000644000004100000410000001723512520074675023543 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'spec_helper' require 'chef/knife/core/cookbook_scm_repo' describe Chef::Knife::CookbookSCMRepo do before do @repo_path = File.join(CHEF_SPEC_DATA, 'cookbooks') @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new @ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {}) @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => 'master') @branch_list = Mixlib::ShellOut.new @branch_list.stdout.replace(<<-BRANCHES) chef-vendor-apache2 chef-vendor-build-essential chef-vendor-dynomite chef-vendor-ganglia chef-vendor-graphite chef-vendor-python chef-vendor-absent-new BRANCHES end it "has a path to the cookbook repo" do expect(@cookbook_repo.repo_path).to eq(@repo_path) end it "has a default branch" do expect(@cookbook_repo.default_branch).to eq('master') end describe "when sanity checking the repo" do it "exits when the directory does not exist" do expect(::File).to receive(:directory?).with(@repo_path).and_return(false) expect {@cookbook_repo.sanity_check}.to raise_error(SystemExit) end describe "and the repo dir exists" do before do allow(::File).to receive(:directory?).with(@repo_path).and_return(true) end it "exits when there is no git repo" do allow(::File).to receive(:directory?).with(/.*\.git/).and_return(false) expect {@cookbook_repo.sanity_check}.to raise_error(SystemExit) end describe "and the repo is a git repo" do before do allow(::File).to receive(:directory?).with(File.join(@repo_path, '.git')).and_return(true) end it "exits when the default branch doesn't exist" do @nobranches = Mixlib::ShellOut.new.tap {|s|s.stdout.replace "\n"} expect(@cookbook_repo).to receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@nobranches) expect {@cookbook_repo.sanity_check}.to raise_error(SystemExit) end describe "and the default branch exists" do before do @master_branch = Mixlib::ShellOut.new @master_branch.stdout.replace "* master\n" expect(@cookbook_repo).to receive(:shell_out!).with("git branch --no-color", :cwd => @repo_path).and_return(@master_branch) end it "exits when the git repo is dirty" do @dirty_status = Mixlib::ShellOut.new @dirty_status.stdout.replace(<<-DIRTY) M chef/lib/chef/knife/cookbook_site_vendor.rb DIRTY expect(@cookbook_repo).to receive(:shell_out!).with('git status --porcelain', :cwd => @repo_path).and_return(@dirty_status) expect {@cookbook_repo.sanity_check}.to raise_error(SystemExit) end describe "and the repo is clean" do before do @clean_status = Mixlib::ShellOut.new.tap {|s| s.stdout.replace("\n")} allow(@cookbook_repo).to receive(:shell_out!).with('git status --porcelain', :cwd => @repo_path).and_return(@clean_status) end it "passes the sanity check" do @cookbook_repo.sanity_check end end end end end end it "resets to default state by checking out the default branch" do expect(@cookbook_repo).to receive(:shell_out!).with('git checkout master', :cwd => @repo_path) @cookbook_repo.reset_to_default_state end it "determines if a the pristine copy branch exists" do expect(@cookbook_repo).to receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list) expect(@cookbook_repo.branch_exists?("chef-vendor-apache2")).to be_truthy expect(@cookbook_repo).to receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list) expect(@cookbook_repo.branch_exists?("chef-vendor-nginx")).to be_falsey end it "determines if a the branch not exists correctly without substring search" do expect(@cookbook_repo).to receive(:shell_out!).twice.with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list) expect(@cookbook_repo).not_to be_branch_exists("chef-vendor-absent") expect(@cookbook_repo).to be_branch_exists("chef-vendor-absent-new") end describe "when the pristine copy branch does not exist" do it "prepares for import by creating the pristine copy branch" do expect(@cookbook_repo).to receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list) expect(@cookbook_repo).to receive(:shell_out!).with('git checkout -b chef-vendor-nginx', :cwd => @repo_path) @cookbook_repo.prepare_to_import("nginx") end end describe "when the pristine copy branch does exist" do it "prepares for import by checking out the pristine copy branch" do expect(@cookbook_repo).to receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list) expect(@cookbook_repo).to receive(:shell_out!).with('git checkout chef-vendor-apache2', :cwd => @repo_path) @cookbook_repo.prepare_to_import("apache2") end end describe "when the pristine copy branch was not updated by the changes" do before do @updates = Mixlib::ShellOut.new @updates.stdout.replace("\n") allow(@cookbook_repo).to receive(:shell_out!).with('git status --porcelain -- apache2', :cwd => @repo_path).and_return(@updates) end it "shows no changes in the pristine copy" do expect(@cookbook_repo.updated?('apache2')).to be_falsey end it "does nothing to finalize the updates" do expect(@cookbook_repo.finalize_updates_to('apache2', '1.2.3')).to be_falsey end end describe "when the pristine copy branch was updated by the changes" do before do @updates = Mixlib::ShellOut.new @updates.stdout.replace(" M cookbooks/apache2/recipes/default.rb\n") allow(@cookbook_repo).to receive(:shell_out!).with('git status --porcelain -- apache2', :cwd => @repo_path).and_return(@updates) end it "shows changes in the pristine copy" do expect(@cookbook_repo.updated?('apache2')).to be_truthy end it "commits the changes to the repo and tags the commit" do expect(@cookbook_repo).to receive(:shell_out!).with("git add apache2", :cwd => @repo_path) expect(@cookbook_repo).to receive(:shell_out!).with("git commit -m \"Import apache2 version 1.2.3\" -- apache2", :cwd => @repo_path) expect(@cookbook_repo).to receive(:shell_out!).with("git tag -f cookbook-site-imported-apache2-1.2.3", :cwd => @repo_path) expect(@cookbook_repo.finalize_updates_to("apache2", "1.2.3")).to be_truthy end end describe "when a custom default branch is specified" do before do @cookbook_repo = Chef::Knife::CookbookSCMRepo.new(@repo_path, @ui, :default_branch => 'develop') end it "resets to default state by checking out the default branch" do expect(@cookbook_repo).to receive(:shell_out!).with('git checkout develop', :cwd => @repo_path) @cookbook_repo.reset_to_default_state end end end chef-12.3.0/spec/unit/knife/core/subcommand_loader_spec.rb0000644000004100000410000002402612520074675023520 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'spec_helper' describe Chef::Knife::SubcommandLoader do let(:loader) { Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands')) } let(:home) { File.join(CHEF_SPEC_DATA, 'knife-home') } let(:plugin_dir) { File.join(home, '.chef', 'plugins', 'knife') } before do allow(Chef::Platform).to receive(:windows?) { false } Chef::Util::PathHelper.class_variable_set(:@@home_dir, home) end after do Chef::Util::PathHelper.class_variable_set(:@@home_dir, nil) end it "builds a list of the core subcommand file require paths" do expect(loader.subcommand_files).not_to be_empty loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end it "finds files installed via rubygems" do expect(loader.find_subcommands_via_rubygems).to include('chef/knife/node_create') loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} end it "finds files from latest version of installed gems" do gems = [ double('knife-ec2-0.5.12') ] gem_files = [ '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_base.rb', '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_otherstuff.rb' ] expect($LOAD_PATH).to receive(:map).and_return([]) if Gem::Specification.respond_to? :latest_specs expect(Gem::Specification).to receive(:latest_specs).with(true).and_return(gems) expect(gems[0]).to receive(:matches_for_glob).with(/chef\/knife\/\*\.rb\{(.*),\.rb,(.*)\}/).and_return(gem_files) else expect(Gem.source_index).to receive(:latest_specs).with(true).and_return(gems) expect(gems[0]).to receive(:require_paths).twice.and_return(['lib']) expect(gems[0]).to receive(:full_gem_path).and_return('/usr/lib/ruby/gems/knife-ec2-0.5.12') expect(Dir).to receive(:[]).with('/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb').and_return(gem_files) end expect(loader).to receive(:find_subcommands_via_dirglob).and_return({}) expect(loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files) end it "finds files using a dirglob when rubygems is not available" do expect(loader.find_subcommands_via_dirglob).to include('chef/knife/node_create') loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])} end it "finds user-specific subcommands in the user's ~/.chef directory" do expected_command = File.join(home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb') expect(loader.site_subcommands).to include(expected_command) end it "finds repo specific subcommands by searching for a .chef directory" do expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb') expect(loader.site_subcommands).to include(expected_command) end # https://github.com/opscode/chef-dk/issues/227 # # `knife` in ChefDK isn't from a gem install, it's directly run from a clone # of the source, but there can be one or more versions of chef also installed # as a gem. If the gem install contains a command that doesn't exist in the # source tree of the "primary" chef install, it can be loaded and cause an # error. We also want to ensure that we only load builtin commands from the # "primary" chef install. context "when a different version of chef is also installed as a gem" do let(:all_found_commands) do [ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb", "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb", "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb", # We use the fake version 1.0.0 because that version doesn't exist, # which ensures it won't ever equal "chef-#{Chef::VERSION}" "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/bootstrap.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_bulk_delete.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_create.rb", # Test that we don't accept a version number that is different only in # trailing characters, e.g. we are running Chef 12.0.0 but there is a # Chef 12.0.0.rc.0 gem also: "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.rc.0/lib/chef/knife/thing.rb", # This command is "extra" compared to what's in the embedded/apps/chef install: "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/data_bag_secret_options.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb", # These are fake commands that have names designed to test that the # regex is strict enough "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb", # In a real scenario, we'd use rubygems APIs to only select the most # recent gem, but for this test we want to check that we're doing the # right thing both when the plugin version matches and does not match # the current chef version. Looking at # `SubcommandLoader::MATCHES_THIS_CHEF_GEM` and # `SubcommandLoader::MATCHES_CHEF_GEM` should make it clear why we want # to test these two cases. "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb" ] end let(:expected_valid_commands) do [ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb", "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb", "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb", "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb" ] end before do expect(loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands) expect(loader).to receive(:find_subcommands_via_dirglob).and_return({}) end it "ignores commands from the non-matching gem install" do expect(loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands) end end describe "finding 3rd party plugins" do let(:home) { "/home/alice" } let(:manifest_path) { home + "/.chef/plugin_manifest.json" } context "when there is not a ~/.chef/plugin_manifest.json file" do before do allow(File).to receive(:exist?).with(manifest_path).and_return(false) end it "searches rubygems for plugins" do if Gem::Specification.respond_to?(:latest_specs) expect(Gem::Specification).to receive(:latest_specs).and_call_original else expect(Gem.source_index).to receive(:latest_specs).and_call_original end loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end context "and HOME environment variable is not set" do before do allow(Chef::Util::PathHelper).to receive(:home).and_return(nil) end it "searches rubygems for plugins" do if Gem::Specification.respond_to?(:latest_specs) expect(Gem::Specification).to receive(:latest_specs).and_call_original else expect(Gem.source_index).to receive(:latest_specs).and_call_original end loader.subcommand_files.each do |require_path| expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/) end end end end context "when there is a ~/.chef/plugin_manifest.json file" do let(:ec2_server_create_plugin) { "/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_server_create.rb" } let(:manifest_content) do { "plugins" => { "knife-ec2" => { "paths" => [ ec2_server_create_plugin ] } } } end let(:manifest_json) { Chef::JSONCompat.to_json(manifest_content) } before do allow(File).to receive(:exist?).with(manifest_path).and_return(true) allow(File).to receive(:read).with(manifest_path).and_return(manifest_json) end it "uses paths from the manifest instead of searching gems" do expect(Gem::Specification).not_to receive(:latest_specs).and_call_original expect(loader.subcommand_files).to include(ec2_server_create_plugin) end end end end chef-12.3.0/spec/unit/knife/core/ui_spec.rb0000644000004100000410000004277712520074675020474 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Author:: Daniel DeLeo () # Author:: John Keiser () # Copyright:: Copyright (c) 2008, 2011, 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::UI do before do @out, @err, @in = StringIO.new, StringIO.new, StringIO.new @config = { :verbosity => 0, :yes => nil, :format => "summary", } @ui = Chef::Knife::UI.new(@out, @err, @in, @config) end describe "edit" do ruby_for_json = { 'foo' => 'bar' } json_from_ruby = "{\n \"foo\": \"bar\"\n}" json_from_editor = "{\n \"bar\": \"foo\"\n}" ruby_from_editor = { 'bar' => 'foo' } my_editor = "veeeye" temp_path = "/tmp/bar/baz" let(:subject) { @ui.edit_data(ruby_for_json, parse_output) } let(:parse_output) { false } context "when editing is disabled" do before do @ui.config[:disable_editing] = true stub_const("Tempfile", double) # Tempfiles should never be invoked end context "when parse_output is false" do it "returns pretty json string" do expect(subject).to eql(json_from_ruby) end end context "when parse_output is true" do let(:parse_output) { true } it "returns a ruby object" do expect(subject).to eql(ruby_for_json) end end end context "when editing is enabled" do before do @ui.config[:disable_editing] = false @ui.config[:editor] = my_editor @mock = double('Tempfile') expect(@mock).to receive(:sync=).with(true) expect(@mock).to receive(:puts).with(json_from_ruby) expect(@mock).to receive(:close) expect(@mock).to receive(:path).at_least(:once).and_return(temp_path) expect(Tempfile).to receive(:open).with([ 'knife-edit-', '.json' ]).and_yield(@mock) end context "and the editor works" do before do expect(@ui).to receive(:system).with("#{my_editor} #{temp_path}").and_return(true) expect(IO).to receive(:read).with(temp_path).and_return(json_from_editor) end context "when parse_output is false" do it "returns an edited pretty json string" do expect(subject).to eql(json_from_editor) end end context "when parse_output is true" do let(:parse_output) { true } it "returns an edited ruby object" do expect(subject).to eql(ruby_from_editor) end end end context "when running the editor fails with nil" do before do expect(@ui).to receive(:system).with("#{my_editor} #{temp_path}").and_return(nil) expect(IO).not_to receive(:read) end it "throws an exception" do expect{ subject }.to raise_error(RuntimeError) end end context "when running the editor fails with false" do before do expect(@ui).to receive(:system).with("#{my_editor} #{temp_path}").and_return(false) expect(IO).not_to receive(:read) end it "throws an exception" do expect{ subject }.to raise_error(RuntimeError) end end end context "when editing and not stubbing Tempfile (semi-functional test)" do before do @ui.config[:disable_editing] = false @ui.config[:editor] = my_editor @tempfile = Tempfile.new([ 'knife-edit-', '.json' ]) expect(Tempfile).to receive(:open).with([ 'knife-edit-', '.json' ]).and_yield(@tempfile) end context "and the editor works" do before do expect(@ui).to receive(:system).with("#{my_editor} #{@tempfile.path}").and_return(true) expect(IO).to receive(:read).with(@tempfile.path).and_return(json_from_editor) end context "when parse_output is false" do it "returns an edited pretty json string" do expect(subject).to eql(json_from_editor) end it "the tempfile should have mode 0600", :unix_only do # XXX: this looks odd because we're really testing Tempfile.new here expect(File.stat(@tempfile.path).mode & 0777).to eql(0600) expect(subject).to eql(json_from_editor) end end context "when parse_output is true" do let(:parse_output) { true } it "returns an edited ruby object" do expect(subject).to eql(ruby_from_editor) end it "the tempfile should have mode 0600", :unix_only do # XXX: this looks odd because we're really testing Tempfile.new here expect(File.stat(@tempfile.path).mode & 0777).to eql(0600) expect(subject).to eql(ruby_from_editor) end end end end end describe "format_list_for_display" do it "should print the full hash if --with-uri is true" do @ui.config[:with_uri] = true expect(@ui.format_list_for_display({ :marcy => :playground })).to eq({ :marcy => :playground }) end it "should print only the keys if --with-uri is false" do @ui.config[:with_uri] = false expect(@ui.format_list_for_display({ :marcy => :playground })).to eq([ :marcy ]) end end shared_examples "an output mehthod handling IO exceptions" do |method| it "should throw Errno::EIO exceptions" do allow(@out).to receive(:puts).and_raise(Errno::EIO) allow(@err).to receive(:puts).and_raise(Errno::EIO) expect {@ui.send(method, "hi")}.to raise_error(Errno::EIO) end it "should ignore Errno::EPIPE exceptions (CHEF-3516)" do allow(@out).to receive(:puts).and_raise(Errno::EPIPE) allow(@err).to receive(:puts).and_raise(Errno::EPIPE) expect {@ui.send(method, "hi")}.to raise_error(SystemExit) end it "should throw Errno::EPIPE exceptions with -VV (CHEF-3516)" do @config[:verbosity] = 2 allow(@out).to receive(:puts).and_raise(Errno::EPIPE) allow(@err).to receive(:puts).and_raise(Errno::EPIPE) expect {@ui.send(method, "hi")}.to raise_error(Errno::EPIPE) end end describe "output" do it_behaves_like "an output mehthod handling IO exceptions", :output it "formats strings appropriately" do @ui.output("hi") expect(@out.string).to eq("hi\n") end it "formats hashes appropriately" do @ui.output({'hi' => 'a', 'lo' => 'b' }) expect(@out.string).to eq < 'b', 'c' => 'd' }, { 'x' => 'y' }, { 'm' => 'n', 'o' => 'p' }]) expect(@out.string).to eq < [], 'b' => 'c' }) expect(@out.string).to eq < [ 'foo' ], 'b' => 'c' }) expect(@out.string).to eq < [ 'foo', 'bar' ], 'b' => 'c' }) expect(@out.string).to eq < [ [ 'foo' ] ], 'b' => 'c' }) expect(@out.string).to eq < [ [ 'foo', 'bar' ], [ 'baz', 'bjork' ] ], 'b' => 'c' }) # XXX: using a HEREDOC at this point results in a line with required spaces which auto-whitespace removal settings # on editors will remove and will break this test. expect(@out.string).to eq("a:\n foo\n bar\n \n baz\n bjork\nb: c\n") end it "formats hashes with hash values appropriately" do @ui.output({ 'a' => { 'aa' => 'bb', 'cc' => 'dd' }, 'b' => 'c' }) expect(@out.string).to eq < { }, 'b' => 'c' }) expect(@out.string).to eq < :go } expect(@ui.format_for_display(input)).to eq(input) end describe "with --attribute passed" do it "should return the deeply nested attribute" do input = { "gi" => { "go" => "ge" }, "id" => "sample-data-bag-item" } @ui.config[:attribute] = "gi.go" expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { "gi.go" => "ge" } }) end it "should return multiple attributes" do input = { "gi" => "go", "hi" => "ho", "id" => "sample-data-bag-item" } @ui.config[:attribute] = ["gi", "hi"] expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { "gi" => "go", "hi"=> "ho" } }) end it "should handle attributes named the same as methods" do input = { "keys" => "values", "hi" => "ho", "id" => "sample-data-bag-item" } @ui.config[:attribute] = "keys" expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { "keys" => "values" } }) end it "should handle nested attributes named the same as methods" do input = { "keys" => {"keys" => "values"}, "hi" => "ho", "id" => "sample-data-bag-item" } @ui.config[:attribute] = "keys.keys" expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { "keys.keys" => "values" } }) end end describe "with --run-list passed" do it "should return the run list" do input = Chef::Node.new input.name("sample-node") input.run_list("role[monkey]", "role[churchmouse]") @ui.config[:run_list] = true response = @ui.format_for_display(input) expect(response["sample-node"]["run_list"][0]).to eq("role[monkey]") expect(response["sample-node"]["run_list"][1]).to eq("role[churchmouse]") end end end describe "format_cookbook_list_for_display" do before(:each) do @item = { "cookbook_name" => { "url" => "http://url/cookbooks/cookbook", "versions" => [ { "version" => "3.0.0", "url" => "http://url/cookbooks/3.0.0" }, { "version" => "2.0.0", "url" => "http://url/cookbooks/2.0.0" }, { "version" => "1.0.0", "url" => "http://url/cookbooks/1.0.0" } ] } } end it "should return an array of the cookbooks with versions" do expected_response = [ "cookbook_name 3.0.0 2.0.0 1.0.0" ] response = @ui.format_cookbook_list_for_display(@item) expect(response).to eq(expected_response) end describe "with --with-uri" do it "should return the URIs" do response = { "cookbook_name"=>{ "1.0.0" => "http://url/cookbooks/1.0.0", "2.0.0" => "http://url/cookbooks/2.0.0", "3.0.0" => "http://url/cookbooks/3.0.0"} } @ui.config[:with_uri] = true expect(@ui.format_cookbook_list_for_display(@item)).to eq(response) end end context "when running on Windows" do before(:each) do stdout = double('StringIO', :tty? => true) allow(@ui).to receive(:stdout).and_return(stdout) allow(Chef::Platform).to receive(:windows?) { true } Chef::Config.reset end after(:each) do Chef::Config.reset end it "should have color set to true if knife config has color explicitly set to true" do Chef::Config[:color] = true @ui.config[:color] = true expect(@ui.color?).to eql(true) end it "should have color set to false if knife config has color explicitly set to false" do Chef::Config[:color] = false expect(@ui.color?).to eql(false) end it "should not have color set to false by default" do expect(@ui.color?).to eql(false) end end end describe "confirm" do let(:stdout) {StringIO.new} let(:output) {stdout.string} let(:question) { "monkeys rule" } let(:answer) { 'y' } let(:default_choice) { nil } let(:append_instructions) { true } def run_confirm allow(@ui).to receive(:stdout).and_return(stdout) allow(@ui.stdin).to receive(:readline).and_return(answer) @ui.confirm(question, append_instructions, default_choice) end def run_confirm_without_exit allow(@ui).to receive(:stdout).and_return(stdout) allow(@ui.stdin).to receive(:readline).and_return(answer) @ui.confirm_without_exit(question, append_instructions, default_choice) end shared_examples_for "confirm with positive answer" do it "confirm should return true" do expect(run_confirm).to be_truthy end it "confirm_without_exit should return true" do expect(run_confirm_without_exit).to be_truthy end end shared_examples_for "confirm with negative answer" do it "confirm should exit 3" do expect { run_confirm }.to raise_error(SystemExit) { |e| expect(e.status).to eq(3) } end it "confirm_without_exit should return false" do expect(run_confirm_without_exit).to be_falsey end end describe "with default choice set to true" do let(:default_choice) { true } it "should show 'Y/n' in the instructions" do run_confirm expect(output).to include("Y/n") end describe "with empty answer" do let(:answer) { "" } it_behaves_like "confirm with positive answer" end describe "with answer N " do let(:answer) { "N" } it_behaves_like "confirm with negative answer" end end describe "with default choice set to false" do let(:default_choice) { false } it "should show 'y/N' in the instructions" do run_confirm expect(output).to include("y/N") end describe "with empty answer" do let(:answer) { "" } it_behaves_like "confirm with negative answer" end describe "with answer N " do let(:answer) { "Y" } it_behaves_like "confirm with positive answer" end end ["Y", "y"].each do |answer| describe "with answer #{answer}" do let(:answer) { answer } it_behaves_like "confirm with positive answer" end end ["N", "n"].each do |answer| describe "with answer #{answer}" do let(:answer) { answer } it_behaves_like "confirm with negative answer" end end describe "with --y or --yes passed" do it "should return true" do @ui.config[:yes] = true expect(run_confirm).to be_truthy expect(output).to eq("") end end end describe "when asking for free-form user input" do it "asks a question and returns the answer provided by the user" do out = StringIO.new allow(@ui).to receive(:stdout).and_return(out) allow(@ui).to receive(:stdin).and_return(StringIO.new("http://mychefserver.example.com\n")) expect(@ui.ask_question("your chef server URL?")).to eq("http://mychefserver.example.com") expect(out.string).to eq("your chef server URL?") end it "suggests a default setting and returns the default when the user's response only contains whitespace" do out = StringIO.new allow(@ui).to receive(:stdout).and_return(out) allow(@ui).to receive(:stdin).and_return(StringIO.new(" \n")) expect(@ui.ask_question("your chef server URL? ", :default => 'http://localhost:4000')).to eq("http://localhost:4000") expect(out.string).to eq("your chef server URL? [http://localhost:4000] ") end end end chef-12.3.0/spec/unit/knife/core/bootstrap_context_spec.rb0000644000004100000410000001674412520074675023633 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'spec_helper' require 'chef/knife/core/bootstrap_context' describe Chef::Knife::Core::BootstrapContext do let(:config) { {:foo => :bar} } let(:run_list) { Chef::RunList.new('recipe[tmux]', 'role[base]') } let(:chef_config) do { :validation_key => File.join(CHEF_SPEC_DATA, 'ssl', 'private_key.pem'), :chef_server_url => 'http://chef.example.com:4444', :validation_client_name => 'chef-validator-testing' } end let(:secret) { nil } subject(:bootstrap_context) { described_class.new(config, run_list, chef_config, secret) } it "initializes with Chef 11 parameters" do expect{described_class.new(config, run_list, chef_config)}.not_to raise_error end it "runs chef with the first-boot.json in the _default environment" do expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -E _default" end describe "when in verbosity mode" do let(:config) { {:verbosity => 2} } it "adds '-l debug' when verbosity is >= 2" do expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -l debug -E _default" end end it "reads the validation key" do expect(bootstrap_context.validation_key).to eq IO.read(File.join(CHEF_SPEC_DATA, 'ssl', 'private_key.pem')) end it "generates the config file data" do expected=<<-EXPECTED log_location STDOUT chef_server_url "http://chef.example.com:4444" validation_client_name "chef-validator-testing" # Using default node name (fqdn) EXPECTED expect(bootstrap_context.config_content).to eq expected end it "does not set a default log_level" do expect(bootstrap_context.config_content).not_to match(/log_level/) end describe "alternate chef-client path" do let(:chef_config){ {:chef_client_path => '/usr/local/bin/chef-client'} } it "runs chef-client from another path when specified" do expect(bootstrap_context.start_chef).to eq "/usr/local/bin/chef-client -j /etc/chef/first-boot.json -E _default" end end describe "validation key path that contains a ~" do let(:chef_config){ {:validation_key => '~/my.key'} } it "reads the validation key when it contains a ~" do expect(File).to receive(:exist?).with(File.expand_path("my.key", ENV['HOME'])).and_return(true) expect(IO).to receive(:read).with(File.expand_path("my.key", ENV['HOME'])) bootstrap_context.validation_key end end describe "when an explicit node name is given" do let(:config){ {:chef_node_name => 'foobar.example.com' }} it "sets the node name in the client.rb" do expect(bootstrap_context.config_content).to match(/node_name "foobar\.example\.com"/) end end describe "when bootstrapping into a specific environment" do let(:chef_config){ {:environment => "prodtastic"} } it "starts chef in the configured environment" do expect(bootstrap_context.start_chef).to eq('chef-client -j /etc/chef/first-boot.json -E prodtastic') end end describe "when JSON attributes are given" do let(:config) { {:first_boot_attributes => {:baz => :quux}} } it "adds the attributes to first_boot" do expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({:baz => :quux, :run_list => run_list})) end end describe "when JSON attributes are NOT given" do it "sets first_boot equal to run_list" do expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({:run_list => run_list})) end end describe "when an encrypted_data_bag_secret is provided" do let(:secret) { "supersekret" } it "reads the encrypted_data_bag_secret" do expect(bootstrap_context.encrypted_data_bag_secret).to eq "supersekret" end end describe "to support compatibility with existing templates" do it "sets the @config instance variable" do expect(bootstrap_context.instance_variable_get(:@config)).to eq config end it "sets the @run_list instance variable" do expect(bootstrap_context.instance_variable_get(:@run_list)).to eq run_list end end describe "when a bootstrap_version is specified" do let(:chef_config) do { :knife => {:bootstrap_version => "11.12.4" } } end it "should send the full version to the installer" do expect(bootstrap_context.latest_current_chef_version_string).to eq("-v 11.12.4") end end describe "when a pre-release bootstrap_version is specified" do let(:chef_config) do { :knife => {:bootstrap_version => "11.12.4.rc.0" } } end it "should send the full version to the installer and set the pre-release flag" do expect(bootstrap_context.latest_current_chef_version_string).to eq("-v 11.12.4.rc.0 -p") end end describe "when a bootstrap_version is not specified" do it "should send the latest current to the installer" do # Intentionally hard coded in order not to replicate the logic. expect(bootstrap_context.latest_current_chef_version_string).to eq("-v #{Chef::VERSION.to_i}") end end describe "ssl_verify_mode" do it "isn't set in the config_content by default" do expect(bootstrap_context.config_content).not_to include("ssl_verify_mode") end describe "when configured in config" do let(:chef_config) do { :knife => {:ssl_verify_mode => :verify_peer} } end it "uses the config value" do expect(bootstrap_context.config_content).to include("ssl_verify_mode :verify_peer") end describe "when configured via CLI" do let(:config) {{:node_ssl_verify_mode => "none"}} it "uses CLI value" do expect(bootstrap_context.config_content).to include("ssl_verify_mode :verify_none") end end end end describe "verify_api_cert" do it "isn't set in the config_content by default" do expect(bootstrap_context.config_content).not_to include("verify_api_cert") end describe "when configured in config" do let(:chef_config) do { :knife => {:verify_api_cert => :false} } end it "uses the config value" do expect(bootstrap_context.config_content).to include("verify_api_cert false") end describe "when configured via CLI" do let(:config) {{:node_verify_api_cert => true}} it "uses CLI value" do expect(bootstrap_context.config_content).to include("verify_api_cert true") end end end end describe "prerelease" do it "isn't set in the config_content by default" do expect(bootstrap_context.config_content).not_to include("prerelease") end describe "when configured via cli" do let(:config) {{:prerelease => true}} it "uses CLI value" do expect(bootstrap_context.latest_current_chef_version_string).to eq("-p") end end end end chef-12.3.0/spec/unit/knife/role_show_spec.rb0000644000004100000410000000351512520074675021113 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Lamont Granquist # 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::RoleShow do let(:role) { 'base' } let(:knife) do knife = Chef::Knife::RoleShow.new knife.name_args = [ role ] knife end let(:role_mock) { double('role_mock') } describe 'run' do it 'should list the role' do expect(Chef::Role).to receive(:load).with('base').and_return(role_mock) expect(knife).to receive(:format_for_display).with(role_mock) knife.run end it 'should pretty print json' do knife.config[:format] = 'json' stdout = StringIO.new allow(knife.ui).to receive(:stdout).and_return(stdout) fake_role_contents = {"foo"=>"bar", "baz"=>"qux"} expect(Chef::Role).to receive(:load).with('base').and_return(fake_role_contents) knife.run expect(stdout.string).to eql("{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}\n") end context "without a role name" do let(:role) { } it 'should print usage and exit when a role name is not provided' do expect(knife).to receive(:show_usage) expect(knife.ui).to receive(:fatal) expect { knife.run }.to raise_error(SystemExit) end end end end chef-12.3.0/spec/unit/knife/ssh_spec.rb0000644000004100000410000003514012520074675017706 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'net/ssh' require 'net/ssh/multi' describe Chef::Knife::Ssh do before(:each) do Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" end before do @knife = Chef::Knife::Ssh.new @knife.merge_configs @knife.config[:attribute] = "fqdn" @node_foo = Chef::Node.new @node_foo.automatic_attrs[:fqdn] = "foo.example.org" @node_foo.automatic_attrs[:ipaddress] = "10.0.0.1" @node_bar = Chef::Node.new @node_bar.automatic_attrs[:fqdn] = "bar.example.org" @node_bar.automatic_attrs[:ipaddress] = "10.0.0.2" end describe "#configure_session" do context "manual is set to false (default)" do before do @knife.config[:manual] = false @query = Chef::Search::Query.new end def configure_query(node_array) allow(@query).to receive(:search).and_return([node_array]) allow(Chef::Search::Query).to receive(:new).and_return(@query) end def self.should_return_specified_attributes it "returns an array of the attributes specified on the command line OR config file, if only one is set" do @knife.config[:attribute] = "ipaddress" @knife.config[:attribute_from_cli] = "ipaddress" configure_query([@node_foo, @node_bar]) expect(@knife).to receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]]) @knife.configure_session end it "returns an array of the attributes specified on the command line even when a config value is set" do @knife.config[:attribute] = "config_file" # this value will be the config file @knife.config[:attribute_from_cli] = "ipaddress" # this is the value of the command line via #configure_attribute configure_query([@node_foo, @node_bar]) expect(@knife).to receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]]) @knife.configure_session end end it "searchs for and returns an array of fqdns" do configure_query([@node_foo, @node_bar]) expect(@knife).to receive(:session_from_list).with([ ['foo.example.org', nil], ['bar.example.org', nil] ]) @knife.configure_session end should_return_specified_attributes context "when cloud hostnames are available" do before do @node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com" @node_bar.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-2.compute-1.amazonaws.com" end it "returns an array of cloud public hostnames" do configure_query([@node_foo, @node_bar]) expect(@knife).to receive(:session_from_list).with([ ['ec2-10-0-0-1.compute-1.amazonaws.com', nil], ['ec2-10-0-0-2.compute-1.amazonaws.com', nil] ]) @knife.configure_session end should_return_specified_attributes end context "when cloud hostnames are available but empty" do before do @node_foo.automatic_attrs[:cloud][:public_hostname] = '' @node_bar.automatic_attrs[:cloud][:public_hostname] = '' end it "returns an array of fqdns" do configure_query([@node_foo, @node_bar]) expect(@knife).to receive(:session_from_list).with([ ['foo.example.org', nil], ['bar.example.org', nil] ]) @knife.configure_session end should_return_specified_attributes end it "should raise an error if no host are found" do configure_query([ ]) expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:exit).with(10) @knife.configure_session end context "when there are some hosts found but they do not have an attribute to connect with" do before do allow(@query).to receive(:search).and_return([[@node_foo, @node_bar]]) @node_foo.automatic_attrs[:fqdn] = nil @node_bar.automatic_attrs[:fqdn] = nil allow(Chef::Search::Query).to receive(:new).and_return(@query) end it "should raise a specific error (CHEF-3402)" do expect(@knife.ui).to receive(:fatal).with(/^2 nodes found/) expect(@knife).to receive(:exit).with(10) @knife.configure_session end end end context "manual is set to true" do before do @knife.config[:manual] = true end it "returns an array of provided values" do @knife.instance_variable_set(:@name_args, ["foo.example.org bar.example.org"]) expect(@knife).to receive(:session_from_list).with(['foo.example.org', 'bar.example.org']) @knife.configure_session end end end describe "#configure_attribute" do before do Chef::Config[:knife][:ssh_attribute] = nil @knife.config[:attribute] = nil end it "should return fqdn by default" do @knife.configure_attribute expect(@knife.config[:attribute]).to eq("fqdn") end it "should return the value set in the configuration file" do Chef::Config[:knife][:ssh_attribute] = "config_file" @knife.configure_attribute expect(@knife.config[:attribute]).to eq("config_file") end it "should return the value set on the command line" do @knife.config[:attribute] = "command_line" @knife.configure_attribute expect(@knife.config[:attribute]).to eq("command_line") end it "should set attribute_from_cli to the value of attribute from the command line" do @knife.config[:attribute] = "command_line" @knife.configure_attribute expect(@knife.config[:attribute]).to eq("command_line") expect(@knife.config[:attribute_from_cli]).to eq("command_line") end it "should prefer the command line over the config file for the value of attribute_from_cli" do Chef::Config[:knife][:ssh_attribute] = "config_file" @knife.config[:attribute] = "command_line" @knife.configure_attribute expect(@knife.config[:attribute]).to eq("command_line") expect(@knife.config[:attribute_from_cli]).to eq("command_line") end end describe "#session_from_list" do before :each do @knife.instance_variable_set(:@longest, 0) ssh_config = {:timeout => 50, :user => "locutus", :port => 23 } allow(Net::SSH).to receive(:configuration_for).with('the.b.org').and_return(ssh_config) end it "uses the port from an ssh config file" do @knife.session_from_list([['the.b.org', nil]]) expect(@knife.session.servers[0].port).to eq(23) end it "uses the port from a cloud attr" do @knife.session_from_list([['the.b.org', 123]]) expect(@knife.session.servers[0].port).to eq(123) end it "uses the user from an ssh config file" do @knife.session_from_list([['the.b.org', 123]]) expect(@knife.session.servers[0].user).to eq("locutus") end end describe "#ssh_command" do let(:execution_channel) { double(:execution_channel, :on_data => nil) } let(:session_channel) { double(:session_channel, :request_pty => nil)} let(:execution_channel2) { double(:execution_channel, :on_data => nil) } let(:session_channel2) { double(:session_channel, :request_pty => nil)} let(:session) { double(:session, :loop => nil) } let(:command) { "false" } before do expect(execution_channel). to receive(:on_request). and_yield(nil, double(:data_stream, :read_long => exit_status)) expect(session_channel). to receive(:exec). with(command). and_yield(execution_channel, true) expect(execution_channel2). to receive(:on_request). and_yield(nil, double(:data_stream, :read_long => exit_status2)) expect(session_channel2). to receive(:exec). with(command). and_yield(execution_channel2, true) expect(session). to receive(:open_channel). and_yield(session_channel). and_yield(session_channel2) end context "both connections return 0" do let(:exit_status) { 0 } let(:exit_status2) { 0 } it "returns a 0 exit code" do expect(@knife.ssh_command(command, session)).to eq(0) end end context "the first connection returns 1 and the second returns 0" do let(:exit_status) { 1 } let(:exit_status2) { 0 } it "returns a non-zero exit code" do expect(@knife.ssh_command(command, session)).to eq(1) end end context "the first connection returns 1 and the second returns 2" do let(:exit_status) { 1 } let(:exit_status2) { 2 } it "returns a non-zero exit code" do expect(@knife.ssh_command(command, session)).to eq(2) end end end describe "#run" do before do @query = Chef::Search::Query.new expect(@query).to receive(:search).and_return([[@node_foo]]) allow(Chef::Search::Query).to receive(:new).and_return(@query) allow(@knife).to receive(:ssh_command).and_return(exit_code) @knife.name_args = ['*:*', 'false'] end context "with an error" do let(:exit_code) { 1 } it "should exit with a non-zero exit code" do expect(@knife).to receive(:exit).with(exit_code) @knife.run end end context "with no error" do let(:exit_code) { 0 } it "should not exit" do expect(@knife).not_to receive(:exit) @knife.run end end end describe "#configure_password" do before do @knife.config.delete(:ssh_password_ng) @knife.config.delete(:ssh_password) end context "when setting ssh_password_ng from knife ssh" do # in this case ssh_password_ng exists, but ssh_password does not it "should prompt for a password when ssh_passsword_ng is nil" do @knife.config[:ssh_password_ng] = nil expect(@knife).to receive(:get_password).and_return("mysekretpassw0rd") @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end it "should set ssh_password to false if ssh_password_ng is false" do @knife.config[:ssh_password_ng] = false expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to be_falsey end it "should set ssh_password to ssh_password_ng if we set a password" do @knife.config[:ssh_password_ng] = "mysekretpassw0rd" expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end end context "when setting ssh_password from knife bootstrap / knife * server create" do # in this case ssh_password exists, but ssh_password_ng does not it "should set ssh_password to nil when ssh_password is nil" do @knife.config[:ssh_password] = nil expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to be_nil end it "should set ssh_password to false when ssh_password is false" do @knife.config[:ssh_password] = false expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to be_falsey end it "should set ssh_password to ssh_password if we set a password" do @knife.config[:ssh_password] = "mysekretpassw0rd" expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end end context "when setting ssh_password in the config variable" do before(:each) do Chef::Config[:knife][:ssh_password] = "my_knife_passw0rd" end context "when setting ssh_password_ng from knife ssh" do # in this case ssh_password_ng exists, but ssh_password does not it "should prompt for a password when ssh_passsword_ng is nil" do @knife.config[:ssh_password_ng] = nil expect(@knife).to receive(:get_password).and_return("mysekretpassw0rd") @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end it "should set ssh_password to the configured knife.rb value if ssh_password_ng is false" do @knife.config[:ssh_password_ng] = false expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd") end it "should set ssh_password to ssh_password_ng if we set a password" do @knife.config[:ssh_password_ng] = "mysekretpassw0rd" expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end end context "when setting ssh_password from knife bootstrap / knife * server create" do # in this case ssh_password exists, but ssh_password_ng does not it "should set ssh_password to the configured knife.rb value when ssh_password is nil" do @knife.config[:ssh_password] = nil expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd") end it "should set ssh_password to the configured knife.rb value when ssh_password is false" do @knife.config[:ssh_password] = false expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("my_knife_passw0rd") end it "should set ssh_password to ssh_password if we set a password" do @knife.config[:ssh_password] = "mysekretpassw0rd" expect(@knife).not_to receive(:get_password) @knife.configure_password expect(@knife.config[:ssh_password]).to eq("mysekretpassw0rd") end end end end end chef-12.3.0/spec/unit/knife/client_bulk_delete_spec.rb0000644000004100000410000001153612520074675022731 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::ClientBulkDelete do let(:stdout_io) { StringIO.new } let(:stdout) {stdout_io.string} let(:stderr_io) { StringIO.new } let(:stderr) { stderr_io.string } let(:knife) { k = Chef::Knife::ClientBulkDelete.new k.name_args = name_args k.config = option_args allow(k.ui).to receive(:stdout).and_return(stdout_io) allow(k.ui).to receive(:stderr).and_return(stderr_io) allow(k.ui).to receive(:confirm).and_return(knife_confirm) allow(k.ui).to receive(:confirm_without_exit).and_return(knife_confirm) k } let(:name_args) { [ "." ] } let(:option_args) { {} } let(:knife_confirm) { true } let(:nonvalidator_client_names) { %w{tim dan stephen} } let(:nonvalidator_clients) { clients = Hash.new nonvalidator_client_names.each do |client_name| client = Chef::ApiClient.new() client.name(client_name) allow(client).to receive(:destroy).and_return(true) clients[client_name] = client end clients } let(:validator_client_names) { %w{myorg-validator} } let(:validator_clients) { clients = Hash.new validator_client_names.each do |validator_client_name| validator_client = Chef::ApiClient.new() validator_client.name(validator_client_name) allow(validator_client).to receive(:validator).and_return(true) allow(validator_client).to receive(:destroy).and_return(true) clients[validator_client_name] = validator_client end clients } let(:client_names) { nonvalidator_client_names + validator_client_names} let(:clients) { nonvalidator_clients.merge(validator_clients) } before(:each) do allow(Chef::ApiClient).to receive(:list).and_return(clients) end describe "run" do describe "without a regex" do let(:name_args) { [ ] } it "should exit if the regex is not provided" do expect { knife.run }.to raise_error(SystemExit) end end describe "with any clients" do it "should get the list of the clients" do expect(Chef::ApiClient).to receive(:list) knife.run end it "should print the name of the clients" do knife.run client_names.each do |client_name| expect(stdout).to include(client_name) end end it "should confirm you really want to delete them" do expect(knife.ui).to receive(:confirm) knife.run end describe "without --delete-validators" do it "should mention that validator clients wont be deleted" do knife.run expect(stdout).to include("Following clients are validators and will not be deleted.") info = stdout.index "Following clients are validators and will not be deleted." val = stdout.index "myorg-validator" expect(val > info).to be_truthy end it "should only delete nonvalidator clients" do nonvalidator_clients.each_value do |c| expect(c).to receive(:destroy) end validator_clients.each_value do |c| expect(c).not_to receive(:destroy) end knife.run end end describe "with --delete-validators" do let(:option_args) { {:delete_validators => true} } it "should mention that validator clients will be deleted" do knife.run expect(stdout).to include("The following validators will be deleted") end it "should confirm twice" do expect(knife.ui).to receive(:confirm).once expect(knife.ui).to receive(:confirm_without_exit).once knife.run end it "should delete all clients" do clients.each_value do |c| expect(c).to receive(:destroy) end knife.run end end end describe "with some clients" do let(:name_args) { [ "^ti" ] } it "should only delete clients that match the regex" do expect(clients["tim"]).to receive(:destroy) expect(clients["stephen"]).not_to receive(:destroy) expect(clients["dan"]).not_to receive(:destroy) expect(clients["myorg-validator"]).not_to receive(:destroy) knife.run end end end end chef-12.3.0/spec/unit/knife/role_run_list_replace_spec.rb0000644000004100000410000000654012520074675023466 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleRunListReplace do before(:each) do Chef::Config[:role_name] = "will" @setup = Chef::Knife::RoleRunListAdd.new @setup.name_args = [ "will", "role[monkey]", "role[dude]", "role[fixer]" ] @knife = Chef::Knife::RoleRunListReplace.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "role[dude]", "role[person]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list[0]).to eq('role[monkey]') expect(@role.run_list[1]).not_to eq('role[dude]') expect(@role.run_list[1]).to eq('role[person]') expect(@role.run_list[2]).to eq('role[fixer]') expect(@role.run_list[3]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "run with a list of roles and recipes" do it "should replace the items from the run list" do @setup.name_args = [ "will", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will', 'role[monkey]', 'role[gibbon]' ] @knife.run @knife.name_args = [ 'will', 'recipe[duck::type]', 'recipe[duck::mallard]' ] @knife.run expect(@role.run_list).not_to include('role[monkey]') expect(@role.run_list).not_to include('recipe[duck::type]') expect(@role.run_list[0]).to eq('recipe[orange::chicken]') expect(@role.run_list[1]).to eq('role[gibbon]') expect(@role.run_list[2]).to eq('recipe[duck::mallard]') expect(@role.run_list[3]).to eq('role[person]') expect(@role.run_list[4]).to eq('role[bird]') expect(@role.run_list[5]).to eq('role[town]') expect(@role.run_list[6]).to be_nil end end end end chef-12.3.0/spec/unit/knife/index_rebuild_spec.rb0000644000004100000410000000741112520074675021726 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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::IndexRebuild do let(:knife){Chef::Knife::IndexRebuild.new} let(:rest_client){double(Chef::REST)} let(:stub_rest!) do expect(knife).to receive(:rest).and_return(rest_client) end before :each do # This keeps the test output clean allow(knife.ui).to receive(:stdout).and_return(StringIO.new) end context "#grab_api_info" do let(:http_not_found_response) do e = Net::HTTPNotFound.new("1.1", 404, "blah") allow(e).to receive(:[]).with("x-ops-api-info").and_return(api_header_value) e end let(:http_server_exception) do Net::HTTPServerException.new("404: Not Found", http_not_found_response) end before(:each) do stub_rest! allow(rest_client).to receive(:get_rest).and_raise(http_server_exception) end context "against a Chef 11 server" do let(:api_header_value){"flavor=osc;version=11.0.0;erchef=1.2.3"} it "retrieves API information" do expect(knife.grab_api_info).to eq({"flavor" => "osc", "version" => "11.0.0", "erchef" => "1.2.3"}) end end # Chef 11 context "against a Chef 10 server" do let(:api_header_value){nil} it "finds no API information" do expect(knife.grab_api_info).to eq({}) end end # Chef 10 end # grab_api_info context "#unsupported_version?" do context "with Chef 11 API metadata" do it "is unsupported" do expect(knife.unsupported_version?({"version" => "11.0.0", "flavor" => "osc", "erchef" => "1.2.3"})).to be_truthy end it "only truly relies on the version being non-nil" do expect(knife.unsupported_version?({"version" => "1", "flavor" => "osc", "erchef" => "1.2.3"})).to be_truthy end end context "with Chef 10 API metadata" do it "is supported" do # Chef 10 will have no metadata expect(knife.unsupported_version?({})).to be_falsey end end end # unsupported_version? context "Simulating a 'knife index rebuild' run" do before :each do expect(knife).to receive(:grab_api_info).and_return(api_info) server_specific_stubs! end context "against a Chef 11 server" do let(:api_info) do {"flavor" => "osc", "version" => "11.0.0", "erchef" => "1.2.3" } end let(:server_specific_stubs!) do expect(knife).to receive(:unsupported_server_message).with(api_info) expect(knife).to receive(:exit).with(1) end it "should not be allowed" do knife.run end end context "against a Chef 10 server" do let(:api_info){ {} } let(:server_specific_stubs!) do stub_rest! expect(rest_client).to receive(:post_rest).with("/search/reindex", {}).and_return("representative output") expect(knife).not_to receive(:unsupported_server_message) expect(knife).to receive(:deprecated_server_message) expect(knife).to receive(:nag) expect(knife).to receive(:output).with("representative output") end it "should be allowed" do knife.run end end end end chef-12.3.0/spec/unit/knife/role_bulk_delete_spec.rb0000644000004100000410000000460012520074675022406 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::RoleBulkDelete do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleBulkDelete.new @knife.config = { :print_after => nil } @knife.name_args = ["."] @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:confirm).and_return(true) @roles = Hash.new %w{dev staging production}.each do |role_name| role = Chef::Role.new() role.name(role_name) allow(role).to receive(:destroy).and_return(true) @roles[role_name] = role end allow(Chef::Role).to receive(:list).and_return(@roles) end describe "run" do it "should get the list of the roles" do expect(Chef::Role).to receive(:list).and_return(@roles) @knife.run end it "should print the roles you are about to delete" do @knife.run expect(@stdout.string).to match(/#{@knife.ui.list(@roles.keys.sort, :columns_down)}/) end it "should confirm you really want to delete them" do expect(@knife.ui).to receive(:confirm) @knife.run end it "should delete each role" do @roles.each_value do |r| expect(r).to receive(:destroy) end @knife.run end it "should only delete roles that match the regex" do @knife.name_args = ["dev"] expect(@roles["dev"]).to receive(:destroy) expect(@roles["staging"]).not_to receive(:destroy) expect(@roles["production"]).not_to receive(:destroy) @knife.run end it "should exit if the regex is not provided" do @knife.name_args = [] expect { @knife.run }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/node_run_list_add_spec.rb0000644000004100000410000001175312520074675022571 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeRunListAdd do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeRunListAdd.new @knife.config = { :after => nil } @knife.name_args = [ "adam", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @node = Chef::Node.new() allow(@node).to receive(:save).and_return(true) allow(Chef::Node).to receive(:load).and_return(@node) end describe "run" do it "should load the node" do expect(Chef::Node).to receive(:load).with("adam") @knife.run end it "should add to the run list" do @knife.run expect(@node.run_list[0]).to eq('role[monkey]') end it "should save the node" do expect(@node).to receive(:save) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.run end describe "with -a or --after specified" do it "should add to the run list after the specified entry" do @node.run_list << "role[acorns]" @node.run_list << "role[barn]" @knife.config[:after] = "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") expect(@node.run_list[2]).to eq("role[barn]") end end describe "with -b or --before specified" do it "should add to the run list before the specified entry" do @node.run_list << "role[acorns]" @node.run_list << "role[barn]" @knife.config[:before] = "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[acorns]") expect(@node.run_list[2]).to eq("role[barn]") end end describe "with both --after and --before specified" do it "exits with an error" do @node.run_list << "role[acorns]" @node.run_list << "role[barn]" @knife.config[:before] = "role[acorns]" @knife.config[:after] = "role[acorns]" expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end describe "with more than one role or recipe" do it "should add to the run list all the entries" do @knife.name_args = [ "adam", "role[monkey],role[duck]" ] @node.run_list << "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") expect(@node.run_list[2]).to eq("role[duck]") end end describe "with more than one role or recipe with space between items" do it "should add to the run list all the entries" do @knife.name_args = [ "adam", "role[monkey], role[duck]" ] @node.run_list << "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") expect(@node.run_list[2]).to eq("role[duck]") end end describe "with more than one role or recipe as different arguments" do it "should add to the run list all the entries" do @knife.name_args = [ "adam", "role[monkey]", "role[duck]" ] @node.run_list << "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") expect(@node.run_list[2]).to eq("role[duck]") end end describe "with more than one role or recipe as different arguments and list separated by commas" do it "should add to the run list all the entries" do @knife.name_args = [ "adam", "role[monkey]", "role[duck],recipe[bird::fly]" ] @node.run_list << "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") expect(@node.run_list[2]).to eq("role[duck]") end end describe "with one role or recipe but with an extraneous comma" do it "should add to the run list one item" do @knife.name_args = [ "adam", "role[monkey]," ] @node.run_list << "role[acorns]" @knife.run expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[monkey]") end end end end chef-12.3.0/spec/unit/knife/role_edit_spec.rb0000644000004100000410000000443712520074675021064 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::RoleEdit do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleEdit.new @knife.config[:print_after] = nil @knife.name_args = [ "adam" ] allow(@knife.ui).to receive(:output).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:save) allow(Chef::Role).to receive(:load).and_return(@role) allow(@knife.ui).to receive(:edit_data).and_return(@role) allow(@knife.ui).to receive(:msg) end describe "run" do it "should load the role" do expect(Chef::Role).to receive(:load).with("adam").and_return(@role) @knife.run end it "should edit the role data" do expect(@knife.ui).to receive(:edit_data).with(@role) @knife.run end it "should save the edited role data" do pansy = Chef::Role.new @role.name("new_role_name") expect(@knife.ui).to receive(:edit_data).with(@role).and_return(pansy) expect(pansy).to receive(:save) @knife.run end it "should not save the unedited role data" do pansy = Chef::Role.new expect(@knife.ui).to receive(:edit_data).with(@role).and_return(pansy) expect(pansy).not_to receive(:save) @knife.run end it "should not print the role" do expect(@knife.ui).not_to receive(:output) @knife.run end describe "with -p or --print-after" do it "should pretty print the role, formatted for display" do @knife.config[:print_after] = true expect(@knife.ui).to receive(:output).with(@role) @knife.run end end end end chef-12.3.0/spec/unit/knife/cookbook_site_share_spec.rb0000644000004100000410000002044412520074675023126 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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/cookbook_uploader' require 'chef/cookbook_site_streaming_uploader' describe Chef::Knife::CookbookSiteShare do before(:each) do @knife = Chef::Knife::CookbookSiteShare.new # Merge default settings in. @knife.merge_configs @knife.name_args = ['cookbook_name', 'AwesomeSausage'] @cookbook = Chef::CookbookVersion.new('cookbook_name') @cookbook_loader = double('Chef::CookbookLoader') allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(true) allow(@cookbook_loader).to receive(:[]).and_return(@cookbook) allow(Chef::CookbookLoader).to receive(:new).and_return(@cookbook_loader) @noauth_rest = double(Chef::REST) allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest) @cookbook_uploader = Chef::CookbookUploader.new('herpderp', :rest => "norest") allow(Chef::CookbookUploader).to receive(:new).and_return(@cookbook_uploader) allow(@cookbook_uploader).to receive(:validate_cookbooks).and_return(true) allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return(Dir.mktmpdir) allow(@knife).to receive(:shell_out!).and_return(true) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe 'run' do before(:each) do allow(@knife).to receive(:do_upload).and_return(true) @category_response = { "name" => "cookbook_name", "category" => "Testing Category" } @bad_category_response = { "error_code" => "NOT_FOUND", "error_messages" => [ "Resource does not exist." ] } end it 'should set true to config[:dry_run] as default' do expect(@knife.config[:dry_run]).to be_falsey end it 'should should print usage and exit when given no arguments' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end it 'should not fail when given only 1 argument and can determine category' do @knife.name_args = ['cookbook_name'] expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name").and_return(@category_response) expect(@knife).to receive(:do_upload) @knife.run end it 'should print error and exit when given only 1 argument and cannot determine category' do @knife.name_args = ['cookbook_name'] expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name").and_return(@bad_category_response) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end it 'should print error and exit when given only 1 argument and Chef::REST throws an exception' do @knife.name_args = ['cookbook_name'] expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" } expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end it 'should check if the cookbook exists' do expect(@cookbook_loader).to receive(:cookbook_exists?) @knife.run end it "should exit and log to error if the cookbook doesn't exist" do allow(@cookbook_loader).to receive(:cookbook_exists?).and_return(false) expect(@knife.ui).to receive(:error) expect { @knife.run }.to raise_error(SystemExit) end if File.exists?('/usr/bin/gnutar') || File.exists?('/bin/gnutar') it 'should use gnutar to make a tarball of the cookbook' do expect(@knife).to receive(:shell_out!) do |args| expect(args.to_s).to match(/gnutar -czf/) end @knife.run end else it 'should make a tarball of the cookbook' do expect(@knife).to receive(:shell_out!) do |args| expect(args.to_s).to match(/tar -czf/) end @knife.run end end it 'should exit and log to error when the tarball creation fails' do allow(@knife).to receive(:shell_out!).and_raise(Chef::Exceptions::Exec) expect(@knife.ui).to receive(:error) expect { @knife.run }.to raise_error(SystemExit) end it 'should upload the cookbook and clean up the tarball' do expect(@knife).to receive(:do_upload) expect(FileUtils).to receive(:rm_rf) @knife.run end context "when the --dry-run flag is specified" do before do allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return("/var/tmp/dummy") @knife.config = { :dry_run => true } allow(@knife).to receive_message_chain(:shell_out!, :stdout).and_return('file') end it "should list files in the tarball" do allow(@knife).to receive(:tar_cmd).and_return("footar") expect(@knife).to receive(:shell_out!).with("footar -czf #{@cookbook.name}.tgz #{@cookbook.name}", {:cwd => "/var/tmp/dummy"}) expect(@knife).to receive(:shell_out!).with("footar -tzf #{@cookbook.name}.tgz", {:cwd => "/var/tmp/dummy"}) @knife.run end it "does not upload the cookbook" do allow(@knife).to receive(:shell_out!).and_return(true) expect(@knife).not_to receive(:do_upload) @knife.run end end end describe 'do_upload' do before(:each) do @upload_response = double('Net::HTTPResponse') allow(Chef::CookbookSiteStreamingUploader).to receive(:post).and_return(@upload_response) @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) allow(File).to receive(:open).and_return(true) end it 'should post the cookbook to "https://supermarket.chef.io"' do response_text = Chef::JSONCompat.to_json({:uri => 'https://supermarket.chef.io/cookbooks/cookbook_name'}) allow(@upload_response).to receive(:body).and_return(response_text) allow(@upload_response).to receive(:code).and_return(201) expect(Chef::CookbookSiteStreamingUploader).to receive(:post).with(/supermarket\.chef\.io/, anything(), anything(), anything()) @knife.run end it 'should alert the user when a version already exists' do response_text = Chef::JSONCompat.to_json({:error_messages => ['Version already exists']}) allow(@upload_response).to receive(:body).and_return(response_text) allow(@upload_response).to receive(:code).and_return(409) expect { @knife.run }.to raise_error(SystemExit) expect(@stderr.string).to match(/ERROR(.+)cookbook already exists/) end it 'should pass any errors on to the user' do response_text = Chef::JSONCompat.to_json({:error_messages => ["You're holding it wrong"]}) allow(@upload_response).to receive(:body).and_return(response_text) allow(@upload_response).to receive(:code).and_return(403) expect { @knife.run }.to raise_error(SystemExit) expect(@stderr.string).to match("ERROR(.*)You're holding it wrong") end it 'should print the body if no errors are exposed on failure' do response_text = Chef::JSONCompat.to_json({:system_error => "Your call was dropped", :reason => "There's a map for that"}) allow(@upload_response).to receive(:body).and_return(response_text) allow(@upload_response).to receive(:code).and_return(500) expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/)#.ordered expect(@knife.ui).to receive(:error).with(/Unknown error/)#.ordered expect { @knife.run }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/node_delete_spec.rb0000644000004100000410000000414312520074675021357 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeDelete do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeDelete.new @knife.config = { :print_after => nil } @knife.name_args = [ "adam" ] allow(@knife).to receive(:output).and_return(true) allow(@knife).to receive(:confirm).and_return(true) @node = Chef::Node.new() allow(@node).to receive(:destroy).and_return(true) allow(Chef::Node).to receive(:load).and_return(@node) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should confirm that you want to delete" do expect(@knife).to receive(:confirm) @knife.run end it "should load the node" do expect(Chef::Node).to receive(:load).with("adam").and_return(@node) @knife.run end it "should delete the node" do expect(@node).to receive(:destroy).and_return(@node) @knife.run end it "should not print the node" do expect(@knife).not_to receive(:output).with("poop") @knife.run end describe "with -p or --print-after" do it "should pretty print the node, formatted for display" do @knife.config[:print_after] = true expect(@knife).to receive(:format_for_display).with(@node).and_return("poop") expect(@knife).to receive(:output).with("poop") @knife.run end end end end chef-12.3.0/spec/unit/knife/data_bag_secret_options_spec.rb0000644000004100000410000001420712520074675023754 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2009-2014 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/knife' require 'chef/config' require 'tempfile' class ExampleDataBagCommand < Chef::Knife include Chef::Knife::DataBagSecretOptions end describe Chef::Knife::DataBagSecretOptions do let(:example_db) do k = ExampleDataBagCommand.new allow(k.ui).to receive(:stdout).and_return(stdout) k end let(:stdout) { StringIO.new } let(:secret) { "abc123SECRET" } let(:secret_file) do sfile = Tempfile.new("encrypted_data_bag_secret") sfile.puts(secret) sfile.flush sfile end after do secret_file.close secret_file.unlink end describe "#validate_secrets" do it "throws an error when provided with both --secret and --secret-file on the CL" do Chef::Config[:knife][:cl_secret_file] = secret_file.path Chef::Config[:knife][:cl_secret] = secret expect(example_db).to receive(:exit).with(1) expect(example_db.ui).to receive(:fatal).with("Please specify only one of --secret, --secret-file") example_db.validate_secrets end it "throws an error when provided with `secret` and `secret_file` in knife.rb" do Chef::Config[:knife][:secret_file] = secret_file.path Chef::Config[:knife][:secret] = secret expect(example_db).to receive(:exit).with(1) expect(example_db.ui).to receive(:fatal).with("Please specify only one of 'secret' or 'secret_file' in your config file") example_db.validate_secrets end end describe "#read_secret" do it "returns the secret first" do Chef::Config[:knife][:cl_secret] = secret expect(example_db).to receive(:config).and_return({ :secret => secret }) expect(example_db.read_secret).to eq(secret) end it "returns the secret_file only if secret does not exist" do Chef::Config[:knife][:cl_secret_file] = secret_file.path expect(example_db).to receive(:config).and_return({ :secret_file => secret_file.path }) expect(Chef::EncryptedDataBagItem).to receive(:load_secret).with(secret_file.path).and_return("secret file contents") expect(example_db.read_secret).to eq("secret file contents") end it "returns the secret from the knife.rb config" do Chef::Config[:knife][:secret_file] = secret_file.path Chef::Config[:knife][:secret] = secret expect(example_db.read_secret).to eq(secret) end it "returns the secret_file from the knife.rb config only if the secret does not exist" do Chef::Config[:knife][:secret_file] = secret_file.path expect(Chef::EncryptedDataBagItem).to receive(:load_secret).with(secret_file.path).and_return("secret file contents") expect(example_db.read_secret).to eq("secret file contents") end end describe "#encryption_secret_provided?" do it "returns true if the secret is passed on the CL" do Chef::Config[:knife][:cl_secret] = secret expect(example_db.encryption_secret_provided?).to eq(true) end it "returns true if the secret_file is passed on the CL" do Chef::Config[:knife][:cl_secret_file] = secret_file.path expect(example_db.encryption_secret_provided?).to eq(true) end it "returns true if --encrypt is passed on the CL and :secret is in config" do expect(example_db).to receive(:config).and_return({ :encrypt => true }) Chef::Config[:knife][:secret] = secret expect(example_db.encryption_secret_provided?).to eq(true) end it "returns true if --encrypt is passed on the CL and :secret_file is in config" do expect(example_db).to receive(:config).and_return({ :encrypt => true }) Chef::Config[:knife][:secret_file] = secret_file.path expect(example_db.encryption_secret_provided?).to eq(true) end it "throws an error if --encrypt is passed and there is not :secret or :secret_file in the config" do expect(example_db).to receive(:config).and_return({ :encrypt => true }) expect(example_db).to receive(:exit).with(1) expect(example_db.ui).to receive(:fatal).with("No secret or secret_file specified in config, unable to encrypt item.") example_db.encryption_secret_provided? end it "returns false if no secret is passed" do expect(example_db).to receive(:config).and_return({}) expect(example_db.encryption_secret_provided?).to eq(false) end it "returns false if --encrypt is not provided and :secret is in the config" do expect(example_db).to receive(:config).and_return({}) Chef::Config[:knife][:secret] = secret expect(example_db.encryption_secret_provided?).to eq(false) end it "returns false if --encrypt is not provided and :secret_file is in the config" do expect(example_db).to receive(:config).and_return({}) Chef::Config[:knife][:secret_file] = secret_file.path expect(example_db.encryption_secret_provided?).to eq(false) end it "returns true if --encrypt is not provided, :secret is in the config and need_encrypt_flag is false" do Chef::Config[:knife][:secret] = secret expect(example_db.encryption_secret_provided_ignore_encrypt_flag?).to eq(true) end it "returns true if --encrypt is not provided, :secret_file is in the config and need_encrypt_flag is false" do Chef::Config[:knife][:secret_file] = secret_file.path expect(example_db.encryption_secret_provided_ignore_encrypt_flag?).to eq(true) end it "returns false if --encrypt is not provided and need_encrypt_flag is false" do expect(example_db.encryption_secret_provided_ignore_encrypt_flag?).to eq(false) end end end chef-12.3.0/spec/unit/knife/data_bag_create_spec.rb0000644000004100000410000000662012520074675022157 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2009-2010 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 'tempfile' describe Chef::Knife::DataBagCreate do let(:knife) do k = Chef::Knife::DataBagCreate.new allow(k).to receive(:rest).and_return(rest) allow(k.ui).to receive(:stdout).and_return(stdout) k end let(:rest) { double("Chef::REST") } let(:stdout) { StringIO.new } let(:bag_name) { "sudoing_admins" } let(:item_name) { "ME" } let(:secret) { "abc123SECRET" } let(:raw_hash) {{ "login_name" => "alphaomega", "id" => item_name }} let(:config) { {} } before do Chef::Config[:node_name] = "webmonkey.example.com" knife.name_args = [bag_name, item_name] allow(knife).to receive(:config).and_return(config) end it "tries to create a data bag with an invalid name when given one argument" do knife.name_args = ['invalid&char'] expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName) expect {knife.run}.to exit_with_code(1) end context "when given one argument" do before do knife.name_args = [bag_name] end it "creates a data bag" do expect(rest).to receive(:post_rest).with("data", {"name" => bag_name}) expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]") knife.run end end context "no secret is specified for encryption" do let(:item) do item = Chef::DataBagItem.from_hash(raw_hash) item.data_bag(bag_name) item end it "creates a data bag item" do expect(knife).to receive(:create_object).and_yield(raw_hash) expect(knife).to receive(:encryption_secret_provided?).and_return(false) expect(rest).to receive(:post_rest).with("data", {'name' => bag_name}).ordered expect(rest).to receive(:post_rest).with("data/#{bag_name}", item).ordered knife.run end end context "a secret is specified for encryption" do let(:encoded_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_hash, secret) } let(:item) do item = Chef::DataBagItem.from_hash(encoded_data) item.data_bag(bag_name) item end it "creates an encrypted data bag item" do expect(knife).to receive(:create_object).and_yield(raw_hash) expect(knife).to receive(:encryption_secret_provided?).and_return(true) expect(knife).to receive(:read_secret).and_return(secret) expect(Chef::EncryptedDataBagItem) .to receive(:encrypt_data_bag_item) .with(raw_hash, secret) .and_return(encoded_data) expect(rest).to receive(:post_rest).with("data", {"name" => bag_name}).ordered expect(rest).to receive(:post_rest).with("data/#{bag_name}", item).ordered knife.run end end end chef-12.3.0/spec/unit/knife/cookbook_site_unshare_spec.rb0000644000004100000410000000501712520074675023470 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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::CookbookSiteUnshare do before(:each) do @knife = Chef::Knife::CookbookSiteUnshare.new @knife.name_args = ['cookbook_name'] allow(@knife).to receive(:confirm).and_return(true) @rest = double('Chef::REST') allow(@rest).to receive(:delete_rest).and_return(true) allow(@knife).to receive(:rest).and_return(@rest) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe 'run' do describe 'with no cookbook argument' do it 'should print the usage and exit' do @knife.name_args = [] expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end end it 'should confirm you want to unshare the cookbook' do expect(@knife).to receive(:confirm) @knife.run end it 'should send a delete request to the cookbook site' do expect(@rest).to receive(:delete_rest) @knife.run end it 'should log an error and exit when forbidden' do exception = double('403 "Forbidden"', :code => '403') allow(@rest).to receive(:delete_rest).and_raise(Net::HTTPServerException.new('403 "Forbidden"', exception)) expect(@knife.ui).to receive(:error) expect { @knife.run }.to raise_error(SystemExit) end it 'should re-raise any non-forbidden errors on delete_rest' do exception = double('500 "Application Error"', :code => '500') allow(@rest).to receive(:delete_rest).and_raise(Net::HTTPServerException.new('500 "Application Error"', exception)) expect { @knife.run }.to raise_error(Net::HTTPServerException) end it 'should log a success message' do expect(@knife.ui).to receive(:info) @knife.run end end end chef-12.3.0/spec/unit/knife/role_env_run_list_add_spec.rb0000644000004100000410000002040212520074675023444 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleEnvRunListAdd do before(:each) do # Chef::Config[:role_name] = "websimian" # Chef::Config[:env_name] = "QA" @knife = Chef::Knife::RoleEnvRunListAdd.new @knife.config = { :after => nil } @knife.name_args = [ "will", "QA", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:save).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should have an empty default run list" do @knife.run expect(@role.run_list[0]).to be_nil end it "should have a QA environment" do @knife.run expect(@role.active_run_list_for('QA')).to eq('QA') end it "should load the role named will" do expect(Chef::Role).to receive(:load).with("will") @knife.run end it "should be able to add an environment specific run list" do @knife.run expect(@role.run_list_for('QA')[0]).to eq('role[monkey]') end it "should save the role" do expect(@role).to receive(:save) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.run end describe "with -a or --after specified" do it "should not create a change if the specified 'after' never comes" do @role.run_list_for("_default") << "role[acorns]" @role.run_list_for("_default") << "role[barn]" @knife.config[:after] = "role[acorns]" @knife.name_args = [ "will", "QA", "role[pad]" ] @knife.run expect(@role.run_list_for("QA")[0]).to be_nil expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[barn]") expect(@role.run_list[2]).to be_nil end it "should add to the run list after the specified entries in the QA run list" do #Setup @role.run_list_for("_default") << "role[acorns]" @role.run_list_for("_default") << "role[barn]" @knife.run @role.run_list_for("QA") << "role[pencil]" @role.run_list_for("QA") << "role[pen]" #Configuration we are testing @knife.config[:after] = "role[pencil]" @knife.name_args = [ "will", "QA", "role[pad]", "role[whackadoo]" ] @knife.run #The actual tests expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[pencil]") expect(@role.run_list_for("QA")[2]).to eq("role[pad]") expect(@role.run_list_for("QA")[3]).to eq("role[whackadoo]") expect(@role.run_list_for("QA")[4]).to eq("role[pen]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[barn]") expect(@role.run_list[2]).to be_nil end end describe "with more than one role or recipe" do it "should add to the QA run list all the entries" do @knife.name_args = [ "will", "QA", "role[monkey],role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[duck]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "with more than one role or recipe with space between items" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "QA", "role[monkey], role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[duck]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "with more than one role or recipe as different arguments" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "QA", "role[monkey]", "role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[duck]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "with more than one role or recipe as different arguments and list separated by comas" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "QA", "role[monkey]", "role[duck],recipe[bird::fly]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[duck]") expect(@role.run_list_for("QA")[2]).to eq("recipe[bird::fly]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "Recipe with version number is allowed" do it "should add to the run list all the entries including the versioned recipe" do @knife.name_args = [ "will", "QA", "role[monkey]", "role[duck],recipe[bird::fly@1.1.3]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to eq("role[duck]") expect(@role.run_list_for("QA")[2]).to eq("recipe[bird::fly@1.1.3]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "with one role or recipe but with an extraneous comma" do it "should add to the run list one item" do @role.run_list_for("_default") << "role[acorns]" @knife.name_args = [ "will", "QA", "role[monkey]," ] @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[monkey]") expect(@role.run_list_for("QA")[1]).to be_nil expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end describe "with more than one command" do it "should be able to the environment run list by running multiple knife commands" do @knife.name_args = [ "will", "QA", "role[blue]," ] @knife.run @knife.name_args = [ "will", "QA", "role[black]," ] @knife.run expect(@role.run_list_for("QA")[0]).to eq("role[blue]") expect(@role.run_list_for("QA")[1]).to eq("role[black]") expect(@role.run_list[0]).to be_nil end end describe "with more than one environment" do it "should add to the run list a second environment in the specific run list" do @role.run_list_for("_default") << "role[acorns]" @knife.name_args = [ "will", "QA", "role[blue]," ] @knife.run @role.run_list_for("QA") << "role[walnuts]" @knife.name_args = [ "will", "PRD", "role[ball]," ] @knife.run @role.run_list_for("PRD") << "role[pen]" expect(@role.run_list_for("QA")[0]).to eq("role[blue]") expect(@role.run_list_for("PRD")[0]).to eq("role[ball]") expect(@role.run_list_for("QA")[1]).to eq("role[walnuts]") expect(@role.run_list_for("PRD")[1]).to eq("role[pen]") expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to be_nil end end end end chef-12.3.0/spec/unit/knife/tag_delete_spec.rb0000644000004100000410000000134312520074675021204 0ustar www-datawww-datarequire 'spec_helper' describe Chef::Knife::TagDelete do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::TagDelete.new @knife.name_args = [ Chef::Config[:node_name], "sadtag" ] @node = Chef::Node.new allow(@node).to receive :save @node.tags << "sadtag" << "happytag" allow(Chef::Node).to receive(:load).and_return @node @stderr = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe "run" do it "can delete tags on a node" do expect(@node.tags).to eq(["sadtag", "happytag"]) @knife.run expect(@node.tags).to eq(["happytag"]) expect(@stderr.string).to match /deleted.+sadtag/i end end end chef-12.3.0/spec/unit/knife/role_run_list_add_spec.rb0000644000004100000410000001504612520074675022604 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleRunListAdd do before(:each) do # Chef::Config[:role_name] = "websimian" # Chef::Config[:env_name] = "QA" @knife = Chef::Knife::RoleRunListAdd.new @knife.config = { :after => nil } @knife.name_args = [ "will", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:save).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should have a run list with the monkey role" do @knife.run expect(@role.run_list[0]).to eq("role[monkey]") end it "should load the role named will" do expect(Chef::Role).to receive(:load).with("will") @knife.run end it "should save the role" do expect(@role).to receive(:save) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.run end describe "with -a or --after specified" do it "should not create a change if the specified 'after' never comes" do @role.run_list_for("_default") << "role[acorns]" @role.run_list_for("_default") << "role[barn]" @knife.config[:after] = "role[tree]" @knife.name_args = [ "will", "role[pad]" ] @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[barn]") expect(@role.run_list[2]).to be_nil end it "should add to the run list after the specified entries in the default run list" do #Setup @role.run_list_for("_default") << "role[acorns]" @role.run_list_for("_default") << "role[barn]" #Configuration we are testing @knife.config[:after] = "role[acorns]" @knife.name_args = [ "will", "role[pad]", "role[whackadoo]" ] @knife.run #The actual tests expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[pad]") expect(@role.run_list[2]).to eq("role[whackadoo]") expect(@role.run_list[3]).to eq("role[barn]") expect(@role.run_list[4]).to be_nil end end describe "with more than one role or recipe" do it "should add to the QA run list all the entries" do @knife.name_args = [ "will", "role[monkey],role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to eq("role[duck]") expect(@role.run_list[3]).to be_nil end end describe "with more than one role or recipe with space between items" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "role[monkey], role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to eq("role[duck]") expect(@role.run_list[3]).to be_nil end end describe "with more than one role or recipe as different arguments" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "role[monkey]", "role[duck]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to eq("role[duck]") expect(@role.run_list[3]).to be_nil end end describe "with more than one role or recipe as different arguments and list separated by comas" do it "should add to the run list all the entries" do @knife.name_args = [ "will", "role[monkey]", "role[duck],recipe[bird::fly]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to eq("role[duck]") expect(@role.run_list[3]).to eq("recipe[bird::fly]") expect(@role.run_list[4]).to be_nil end end describe "Recipe with version number is allowed" do it "should add to the run list all the entries including the versioned recipe" do @knife.name_args = [ "will", "role[monkey]", "role[duck],recipe[bird::fly@1.1.3]" ] @role.run_list_for("_default") << "role[acorns]" @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to eq("role[duck]") expect(@role.run_list[3]).to eq("recipe[bird::fly@1.1.3]") expect(@role.run_list[4]).to be_nil end end describe "with one role or recipe but with an extraneous comma" do it "should add to the run list one item" do @role.run_list_for("_default") << "role[acorns]" @knife.name_args = [ "will", "role[monkey]," ] @knife.run expect(@role.run_list[0]).to eq("role[acorns]") expect(@role.run_list[1]).to eq("role[monkey]") expect(@role.run_list[2]).to be_nil end end describe "with more than one command" do it "should be able to the environment run list by running multiple knife commands" do @knife.name_args = [ "will", "role[blue]," ] @knife.run @knife.name_args = [ "will", "role[black]," ] @knife.run expect(@role.run_list[0]).to eq("role[blue]") expect(@role.run_list[1]).to eq("role[black]") expect(@role.run_list[2]).to be_nil end end end end chef-12.3.0/spec/unit/knife/bootstrap/0000755000004100000410000000000012520074675017564 5ustar www-datawww-datachef-12.3.0/spec/unit/knife/bootstrap/client_builder_spec.rb0000644000004100000410000001526312520074675024116 0ustar www-datawww-data# # Author:: Lamont Granquist ) # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Knife::Bootstrap::ClientBuilder do let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:stdin) { StringIO.new } let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) } let(:knife_config) { {} } let(:chef_config) { {} } let(:node_name) { "bevell.wat" } let(:rest) { double("Chef::REST") } let(:client_builder) { client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(knife_config: knife_config, chef_config: chef_config, ui: ui) allow(client_builder).to receive(:rest).and_return(rest) allow(client_builder).to receive(:node_name).and_return(node_name) client_builder } context "#sanity_check!" do let(:response_404) { OpenStruct.new(:code => '404') } let(:exception_404) { Net::HTTPServerException.new("404 not found", response_404) } context "in cases where the prompting fails" do before do # should fail early in #run expect(client_builder).to_not receive(:create_client!) expect(client_builder).to_not receive(:create_node!) end it "exits when the node exists and the user does not want to delete" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}") expect(ui.stdin).to receive(:readline).and_return('n') expect { client_builder.run }.to raise_error(SystemExit) end it "exits when the client exists and the user does not want to delete" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}").and_raise(exception_404) expect(rest).to receive(:get_rest).with("clients/#{node_name}") expect(ui.stdin).to receive(:readline).and_return('n') expect { client_builder.run }.to raise_error(SystemExit) end end context "in cases where the prompting succeeds" do before do # mock out the rest of #run expect(client_builder).to receive(:create_client!) expect(client_builder).to receive(:create_node!) end it "when both the client and node do not exist it succeeds" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}").and_raise(exception_404) expect(rest).to receive(:get_rest).with("clients/#{node_name}").and_raise(exception_404) expect { client_builder.run }.not_to raise_error end it "when we are allowed to delete an old node" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}") expect(ui.stdin).to receive(:readline).and_return('y') expect(rest).to receive(:get_rest).with("clients/#{node_name}").and_raise(exception_404) expect(rest).to receive(:delete).with("nodes/#{node_name}") expect { client_builder.run }.not_to raise_error end it "when we are allowed to delete an old client" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}").and_raise(exception_404) expect(rest).to receive(:get_rest).with("clients/#{node_name}") expect(ui.stdin).to receive(:readline).and_return('y') expect(rest).to receive(:delete).with("clients/#{node_name}") expect { client_builder.run }.not_to raise_error end it "when we are are allowed to delete both an old client and node" do expect(rest).to receive(:get_rest).with("nodes/#{node_name}") expect(rest).to receive(:get_rest).with("clients/#{node_name}") expect(ui.stdin).to receive(:readline).twice.and_return('y') expect(rest).to receive(:delete).with("nodes/#{node_name}") expect(rest).to receive(:delete).with("clients/#{node_name}") expect { client_builder.run }.not_to raise_error end end end context "#create_client!" do before do # mock out the rest of #run expect(client_builder).to receive(:sanity_check) expect(client_builder).to receive(:create_node!) end it "delegates everything to Chef::ApiClient::Registration" do reg_double = double("Chef::ApiClient::Registration") expect(Chef::ApiClient::Registration).to receive(:new).with(node_name, client_builder.client_path, http_api: rest).and_return(reg_double) expect(reg_double).to receive(:run) client_builder.run end end context "#client_path" do it "has a public API for the temporary client.pem file" do expect(client_builder.client_path).to match(/#{node_name}.pem/) end end context "#create_node!" do before do # mock out the rest of #run expect(client_builder).to receive(:sanity_check) expect(client_builder).to receive(:create_client!) # mock out default node building steps expect(client_builder).to receive(:client_rest).and_return(client_rest) expect(Chef::Node).to receive(:new).with(chef_server_rest: client_rest).and_return(node) expect(node).to receive(:name).with(node_name) expect(node).to receive(:save) end let(:client_rest) { double("Chef::REST (client)") } let(:node) { double("Chef::Node") } it "builds a node with a default run_list of []" do expect(node).to receive(:run_list).with([]) client_builder.run end it "builds a node when the run_list is a string" do knife_config[:run_list] = "role[base],role[app]" expect(node).to receive(:run_list).with(["role[base]", "role[app]"]) client_builder.run end it "builds a node when the run_list is an Array" do knife_config[:run_list] = ["role[base]", "role[app]"] expect(node).to receive(:run_list).with(["role[base]", "role[app]"]) client_builder.run end it "builds a node with first_boot_attributes if they're given" do knife_config[:first_boot_attributes] = {:baz => :quux} expect(node).to receive(:normal_attrs=).with({:baz=>:quux}) expect(node).to receive(:run_list).with([]) client_builder.run end it "builds a node with an environment if its given" do knife_config[:environment] = "production" expect(node).to receive(:environment).with("production") expect(node).to receive(:run_list).with([]) client_builder.run end end end chef-12.3.0/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb0000644000004100000410000001655612520074675024755 0ustar www-datawww-data# # Author:: Lamont Granquist ) # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Knife::Bootstrap::ChefVaultHandler do let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:stdin) { StringIO.new } let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) } let(:knife_config) { {} } let(:node_name) { "bevell.wat" } let(:chef_vault_handler) { chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(knife_config: knife_config, ui: ui) chef_vault_handler } context "when there's no vault option" do it "should report its not doing anything" do expect(chef_vault_handler.doing_chef_vault?).to be false end it "shouldn't do anything" do expect(chef_vault_handler).to_not receive(:sanity_check) expect(chef_vault_handler).to_not receive(:update_bootstrap_vault_json!) chef_vault_handler end end context "when setting chef vault items" do let(:bootstrap_vault_item) { double("ChefVault::Item") } before do expect(chef_vault_handler).to receive(:wait_for_client).and_return(false) expect(chef_vault_handler).to receive(:require_chef_vault!).at_least(:once) expect(bootstrap_vault_item).to receive(:clients).with("name:#{node_name}").at_least(:once) expect(bootstrap_vault_item).to receive(:save).at_least(:once) end context "from knife_config[:bootstrap_vault_item]" do it "sets a single item as a scalar" do knife_config[:bootstrap_vault_item] = { 'vault' => 'item1' } expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets a single item as an array" do knife_config[:bootstrap_vault_item] = { 'vault' => [ 'item1' ] } expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two items as an array" do knife_config[:bootstrap_vault_item] = { 'vault' => [ 'item1', 'item2' ] } expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two vaults from different hash keys" do knife_config[:bootstrap_vault_item] = { 'vault' => [ 'item1', 'item2' ], 'vault2' => [ 'item3' ] } expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault2', 'item3').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end end context "from knife_config[:bootstrap_vault_json]" do it "sets a single item as a scalar" do knife_config[:bootstrap_vault_json] = '{ "vault": "item1" }' expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets a single item as an array" do knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1" ] }' expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two items as an array" do knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ] }' expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two vaults from different hash keys" do knife_config[:bootstrap_vault_json] = '{ "vault": [ "item1", "item2" ], "vault2": [ "item3" ] }' expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault2', 'item3').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end end context "from knife_config[:bootstrap_vault_file]" do def setup_file_contents(json) stringio = StringIO.new(json) knife_config[:bootstrap_vault_file] = "/foo/bar/baz" expect(File).to receive(:read).with(knife_config[:bootstrap_vault_file]).and_return(stringio) end it "sets a single item as a scalar" do setup_file_contents('{ "vault": "item1" }') expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets a single item as an array" do setup_file_contents('{ "vault": [ "item1" ] }') expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two items as an array" do setup_file_contents('{ "vault": [ "item1", "item2" ] }') expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end it "sets two vaults from different hash keys" do setup_file_contents('{ "vault": [ "item1", "item2" ], "vault2": [ "item3" ] }') expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item1').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault', 'item2').and_return(bootstrap_vault_item) expect(chef_vault_handler).to receive(:load_chef_bootstrap_vault_item).with('vault2', 'item3').and_return(bootstrap_vault_item) chef_vault_handler.run(node_name: node_name) end end end end chef-12.3.0/spec/unit/knife/role_run_list_set_spec.rb0000644000004100000410000000550612520074675022647 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleRunListSet do before(:each) do Chef::Config[:role_name] = "will" @setup = Chef::Knife::RoleRunListAdd.new @setup.name_args = [ "will", "role[monkey]", "role[person]", "role[bucket]" ] @knife = Chef::Knife::RoleRunListSet.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "role[owen]", "role[mauntel]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should replace all the items in the runlist with what is specified" do @setup.run @knife.run expect(@role.run_list[0]).to eq("role[owen]") expect(@role.run_list[1]).to eq("role[mauntel]") expect(@role.run_list[2]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "should clear an environmental run list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ "will", "role[coke]", "role[pepsi]" ] @knife.run expect(@role.run_list[0]).to eq("role[coke]") expect(@role.run_list[1]).to eq("role[pepsi]") expect(@role.run_list[2]).to be_nil expect(@role.run_list[3]).to be_nil end end end end chef-12.3.0/spec/unit/knife/node_environment_set_spec.rb0000644000004100000410000000465412520074675023343 0ustar www-datawww-data# # Author:: Jimmy McCrory () # Copyright:: Copyright (c) 2014 Jimmy McCrory # 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::NodeEnvironmentSet do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeEnvironmentSet.new @knife.name_args = [ "adam", "bar" ] allow(@knife).to receive(:output).and_return(true) @node = Chef::Node.new() @node.name("knifetest-node") @node.chef_environment << "foo" allow(@node).to receive(:save).and_return(true) allow(Chef::Node).to receive(:load).and_return(@node) end describe "run" do it "should load the node" do expect(Chef::Node).to receive(:load).with("adam") @knife.run end it "should update the environment" do @knife.run expect(@node.chef_environment).to eq('bar') end it "should save the node" do expect(@node).to receive(:save) @knife.run end it "should print the environment" do expect(@knife).to receive(:output).and_return(true) @knife.run end describe "with no environment" do # Set up outputs for inspection later before(:each) do @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) end it "should exit" do @knife.name_args = [ "adam" ] expect { @knife.run }.to raise_error SystemExit end it "should show the user the usage and an error" do @knife.name_args = [ "adam" ] begin ; @knife.run ; rescue SystemExit ; end expect(@stdout.string).to eq "USAGE: knife node environment set NODE ENVIRONMENT\n" expect(@stderr.string).to eq "FATAL: You must specify a node name and an environment.\n" end end end end chef-12.3.0/spec/unit/knife/cookbook_download_spec.rb0000644000004100000410000002270712520074675022613 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::CookbookDownload do before(:each) do @knife = Chef::Knife::CookbookDownload.new @stderr = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe 'run' do it 'should print usage and exit when a cookbook name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal).with(/must specify a cookbook name/) expect { @knife.run }.to raise_error(SystemExit) end it 'should exit with a fatal error when there is no cookbook on the server' do @knife.name_args = ['foobar', nil] expect(@knife).to receive(:determine_version).and_return(nil) expect(@knife.ui).to receive(:fatal).with('No such cookbook found') expect { @knife.run }.to raise_error(SystemExit) end describe 'with a cookbook name' do before(:each) do @knife.name_args = ['foobar'] @knife.config[:download_directory] = '/var/tmp/chef' @rest_mock = double('rest') allow(@knife).to receive(:rest).and_return(@rest_mock) @manifest_data = { :recipes => [ {'path' => 'recipes/foo.rb', 'url' => 'http://example.org/files/foo.rb'}, {'path' => 'recipes/bar.rb', 'url' => 'http://example.org/files/bar.rb'} ], :templates => [ {'path' => 'templates/default/foo.erb', 'url' => 'http://example.org/files/foo.erb'}, {'path' => 'templates/default/bar.erb', 'url' => 'http://example.org/files/bar.erb'} ], :attributes => [ {'path' => 'attributes/default.rb', 'url' => 'http://example.org/files/default.rb'} ] } @cookbook_mock = double('cookbook') allow(@cookbook_mock).to receive(:version).and_return('1.0.0') allow(@cookbook_mock).to receive(:manifest).and_return(@manifest_data) expect(@rest_mock).to receive(:get_rest).with('cookbooks/foobar/1.0.0'). and_return(@cookbook_mock) end it 'should determine which version if one was not explicitly specified'do allow(@cookbook_mock).to receive(:manifest).and_return({}) expect(@knife).to receive(:determine_version).and_return('1.0.0') expect(File).to receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(false) allow(Chef::CookbookVersion).to receive(:COOKBOOK_SEGEMENTS).and_return([]) @knife.run end describe 'and a version' do before(:each) do @knife.name_args << '1.0.0' @files = @manifest_data.values.map { |v| v.map { |i| i['path'] } }.flatten.uniq @files_mocks = {} @files.map { |f| File.basename(f) }.flatten.uniq.each do |f| @files_mocks[f] = double("#{f}_mock") allow(@files_mocks[f]).to receive(:path).and_return("/var/tmp/#{f}") end end it 'should print an error and exit if the cookbook download directory already exists' do expect(File).to receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(true) expect(@knife.ui).to receive(:fatal).with(/\/var\/tmp\/chef\/foobar-1\.0\.0 exists/i) expect { @knife.run }.to raise_error(SystemExit) end describe 'when downloading the cookbook' do before(:each) do @files.map { |f| File.dirname(f) }.flatten.uniq.each do |dir| expect(FileUtils).to receive(:mkdir_p).with("/var/tmp/chef/foobar-1.0.0/#{dir}"). at_least(:once) end @files_mocks.each_pair do |file, mock| expect(@rest_mock).to receive(:get_rest).with("http://example.org/files/#{file}", true). and_return(mock) end expect(@rest_mock).to receive(:sign_on_redirect=).with(false).at_least(:once) @files.each do |f| expect(FileUtils).to receive(:mv). with("/var/tmp/#{File.basename(f)}", "/var/tmp/chef/foobar-1.0.0/#{f}") end end it "should download the cookbook when the cookbook download directory doesn't exist" do expect(File).to receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(false) @knife.run ['attributes', 'recipes', 'templates'].each do |segment| expect(@stderr.string).to match /downloading #{segment}/im end expect(@stderr.string).to match /downloading foobar cookbook version 1\.0\.0/im expect(@stderr.string).to match /cookbook downloaded to \/var\/tmp\/chef\/foobar-1\.0\.0/im end describe 'with -f or --force' do it 'should remove the existing the cookbook download directory if it exists' do @knife.config[:force] = true expect(File).to receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(true) expect(FileUtils).to receive(:rm_rf).with('/var/tmp/chef/foobar-1.0.0') @knife.run end end end end end end describe 'determine_version' do it 'should return nil if there are no versions' do expect(@knife).to receive(:available_versions).and_return(nil) expect(@knife.determine_version).to eq(nil) expect(@knife.version).to eq(nil) end it 'should return and set the version if there is only one version' do expect(@knife).to receive(:available_versions).at_least(:once).and_return(['1.0.0']) expect(@knife.determine_version).to eq('1.0.0') expect(@knife.version).to eq('1.0.0') end it 'should ask which version to download and return it if there is more than one' do expect(@knife).to receive(:available_versions).at_least(:once).and_return(['1.0.0', '2.0.0']) expect(@knife).to receive(:ask_which_version).and_return('1.0.0') expect(@knife.determine_version).to eq('1.0.0') end describe 'with -N or --latest' do it 'should return and set the version to the latest version' do @knife.config[:latest] = true expect(@knife).to receive(:available_versions).at_least(:once). and_return(['1.0.0', '1.1.0', '2.0.0']) @knife.determine_version expect(@knife.version.to_s).to eq('2.0.0') end end end describe 'available_versions' do before(:each) do @knife.cookbook_name = 'foobar' end it 'should return nil if there are no versions' do expect(Chef::CookbookVersion).to receive(:available_versions). with('foobar'). and_return(nil) expect(@knife.available_versions).to eq(nil) end it 'should return the available versions' do expect(Chef::CookbookVersion).to receive(:available_versions). with('foobar'). and_return(['1.1.0', '2.0.0', '1.0.0']) expect(@knife.available_versions).to eq([Chef::Version.new('1.0.0'), Chef::Version.new('1.1.0'), Chef::Version.new('2.0.0')]) end it 'should avoid multiple API calls to the server' do expect(Chef::CookbookVersion).to receive(:available_versions). once. with('foobar'). and_return(['1.1.0', '2.0.0', '1.0.0']) @knife.available_versions @knife.available_versions end end describe 'ask_which_version' do before(:each) do @knife.cookbook_name = 'foobar' allow(@knife).to receive(:available_versions).and_return(['1.0.0', '1.1.0', '2.0.0']) end it 'should prompt the user to select a version' do prompt = /Which version do you want to download\?.+1\. foobar 1\.0\.0.+2\. foobar 1\.1\.0.+3\. foobar 2\.0\.0.+/m expect(@knife).to receive(:ask_question).with(prompt).and_return('1') @knife.ask_which_version end it "should set the version to the user's selection" do expect(@knife).to receive(:ask_question).and_return('1') @knife.ask_which_version expect(@knife.version).to eq('1.0.0') end it "should print an error and exit if a version wasn't specified" do expect(@knife).to receive(:ask_question).and_return('') expect(@knife.ui).to receive(:error).with(/is not a valid value/i) expect { @knife.ask_which_version }.to raise_error(SystemExit) end it 'should print an error if an invalid choice was selected' do expect(@knife).to receive(:ask_question).and_return('100') expect(@knife.ui).to receive(:error).with(/'100' is not a valid value/i) expect { @knife.ask_which_version }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/ssl_fetch_spec.rb0000644000004100000410000001277212520074675021071 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/knife/ssl_fetch' describe Chef::Knife::SslFetch do let(:name_args) { [] } let(:stdout_io) { StringIO.new } let(:stderr_io) { StringIO.new } def stderr stderr_io.string end def stdout stdout_io.string end subject(:ssl_fetch) do s = Chef::Knife::SslFetch.new s.name_args = name_args allow(s.ui).to receive(:stdout).and_return(stdout_io) allow(s.ui).to receive(:stderr).and_return(stderr_io) s end context "when no arguments are given" do before do Chef::Config.chef_server_url = "https://example.com:8443/chef-server" end it "uses the chef_server_url as the host to fetch" do expect(ssl_fetch.host).to eq("example.com") expect(ssl_fetch.port).to eq(8443) end end context "when a specific URI is given" do let(:name_args) { %w{https://example.test:10443/foo} } it "fetchs the SSL configuration against the given host" do expect(ssl_fetch.host).to eq("example.test") expect(ssl_fetch.port).to eq(10443) end end context "when an invalid URI is given" do let(:name_args) { %w{foo.test} } it "prints an error and exits" do expect { ssl_fetch.run }.to raise_error(SystemExit) expected_stdout=<<-E USAGE: knife ssl fetch [URL] (options) E expected_stderr=<<-E ERROR: Given URI: `foo.test' is invalid E expect(stdout_io.string).to eq(expected_stdout) expect(stderr_io.string).to eq(expected_stderr) end context "and its malformed enough to make URI.parse barf" do let(:name_args) { %w{ftp://lkj\\blah:example.com/blah} } it "prints an error and exits" do expect { ssl_fetch.run }.to raise_error(SystemExit) expected_stdout=<<-E USAGE: knife ssl fetch [URL] (options) E expected_stderr=<<-E ERROR: Given URI: `#{name_args[0]}' is invalid E expect(stdout_io.string).to eq(expected_stdout) expect(stderr_io.string).to eq(expected_stderr) end end end describe "normalizing CNs for use as paths" do it "normalizes '*' to 'wildcard'" do expect(ssl_fetch.normalize_cn("*.example.com")).to eq("wildcard_example_com") end it "normalizes non-alnum and hyphen characters to underscores" do expect(ssl_fetch.normalize_cn("Billy-Bob's Super Awesome CA!")).to eq("Billy-Bob_s_Super_Awesome_CA_") end end describe "fetching the remote cert chain" do let(:name_args) { %w{https://foo.example.com:8443} } let(:tcp_socket) { double(TCPSocket) } let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket) } let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt") } let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) } let(:trusted_certs_dir) { Dir.mktmpdir } def run ssl_fetch.run rescue Exception puts "OUT: #{stdout_io.string}" puts "ERR: #{stderr_io.string}" raise end before do Chef::Config.trusted_certs_dir = trusted_certs_dir end after do FileUtils.rm_rf(trusted_certs_dir) end context "when the TLS connection is successful" do before do expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket) expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket) expect(ssl_socket).to receive(:connect) expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt]) end it "fetches the cert chain and writes the certs to the trusted_certs_dir" do run stored_cert_path = File.join(trusted_certs_dir, "example_local.crt") expect(File).to exist(stored_cert_path) expect(File.read(stored_cert_path)).to eq(File.read(self_signed_crt_path)) end end context "when connecting to a non-SSL service (like HTTP)" do let(:name_args) { %w{http://foo.example.com} } let(:unknown_protocol_error) { OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol") } before do expect(TCPSocket).to receive(:new).with("foo.example.com", 80).and_return(tcp_socket) expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket) expect(ssl_socket).to receive(:connect).and_raise(unknown_protocol_error) expect(ssl_fetch).to receive(:exit).with(1) end it "tells the user their URL is for a non-ssl service" do expected_error_text = <<-ERROR_TEXT ERROR: The service at the given URI (http://foo.example.com) does not accept SSL connections ERROR: Perhaps you meant to connect to 'https://foo.example.com'? ERROR_TEXT run expect(stderr).to include(expected_error_text) end end end end chef-12.3.0/spec/unit/knife/environment_from_file_spec.rb0000644000004100000410000000613212520074675023476 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Seth Falcon () # Copyright:: Copyright 2010 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::EnvironmentFromFile.load_deps describe Chef::Knife::EnvironmentFromFile do before(:each) do allow(Chef::Platform).to receive(:windows?) { false } @knife = Chef::Knife::EnvironmentFromFile.new @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) @knife.name_args = [ "spec.rb" ] @environment = Chef::Environment.new @environment.name("spec") @environment.description("runs the unit tests") @environment.cookbook_versions({"apt" => "= 1.2.3"}) allow(@environment).to receive(:save).and_return true allow(@knife.loader).to receive(:load_from).and_return @environment end describe "run" do it "loads the environment data from a file and saves it" do expect(@knife.loader).to receive(:load_from).with('environments', 'spec.rb').and_return(@environment) expect(@environment).to receive(:save) @knife.run end context "when handling multiple environments" do before(:each) do @env_apple = @environment.dup @env_apple.name("apple") allow(@knife.loader).to receive(:load_from).with("apple.rb").and_return @env_apple end it "loads multiple environments if given" do @knife.name_args = [ "spec.rb", "apple.rb" ] expect(@environment).to receive(:save).twice @knife.run end it "loads all environments with -a" do allow(File).to receive(:expand_path).with("./environments/").and_return("/tmp/environments") allow(Dir).to receive(:glob).with("/tmp/environments/*.{json,rb}").and_return(["spec.rb", "apple.rb"]) @knife.name_args = [] allow(@knife).to receive(:config).and_return({:all => true}) expect(@environment).to receive(:save).twice @knife.run end end it "should not print the environment" do expect(@knife).not_to receive(:output) @knife.run end it "should show usage and exit if not filename is provided" do @knife.name_args = [] expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end describe "with --print-after" do it "should pretty print the environment, formatted for display" do @knife.config[:print_after] = true expect(@knife).to receive(:output) @knife.run end end end end chef-12.3.0/spec/unit/knife/tag_create_spec.rb0000644000004100000410000000124712520074675021210 0ustar www-datawww-datarequire 'spec_helper' describe Chef::Knife::TagCreate do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::TagCreate.new @knife.name_args = [ Chef::Config[:node_name], "happytag" ] @node = Chef::Node.new allow(@node).to receive :save allow(Chef::Node).to receive(:load).and_return @node @stderr = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe "run" do it "can create tags on a node" do @knife.run expect(@node.tags).to eq(["happytag"]) expect(@stderr.string).to match /created tags happytag.+node webmonkey.example.com/i end end end chef-12.3.0/spec/unit/knife/user_show_spec.rb0000644000004100000410000000252212520074675021125 0ustar www-datawww-data# # 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::UserShow do before(:each) do Chef::Knife::UserShow.load_deps @knife = Chef::Knife::UserShow.new @knife.name_args = [ 'my_user' ] @user_mock = double('user_mock') end it 'loads and displays the user' do expect(Chef::User).to receive(:load).with('my_user').and_return(@user_mock) expect(@knife).to 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 = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end chef-12.3.0/spec/unit/knife/user_edit_spec.rb0000644000004100000410000000304312520074675021071 0ustar www-datawww-data# # 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::UserEdit do before(:each) do @stderr = StringIO.new @stdout = StringIO.new Chef::Knife::UserEdit.load_deps @knife = Chef::Knife::UserEdit.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) allow(@knife.ui).to receive(:stdout).and_return(@stdout) @knife.name_args = [ 'my_user' ] @knife.config[:disable_editing] = true end it 'loads and edits the user' do data = { :name => "my_user" } allow(Chef::User).to receive(:load).with("my_user").and_return(data) expect(@knife).to 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 = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end chef-12.3.0/spec/unit/knife/user_reregister_spec.rb0000644000004100000410000000357012520074675022324 0ustar www-datawww-data# # 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::UserReregister do before(:each) do Chef::Knife::UserReregister.load_deps @knife = Chef::Knife::UserReregister.new @knife.name_args = [ 'a_user' ] @user_mock = double('user_mock', :private_key => "private_key") allow(Chef::User).to receive(:load).and_return(@user_mock) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end it 'prints usage and exits when a user name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end it 'reregisters the user and prints the key' do expect(@user_mock).to receive(:reregister).and_return(@user_mock) @knife.run expect(@stdout.string).to match( /private_key/ ) end it 'writes the private key to a file when --file is specified' do expect(@user_mock).to receive(:reregister).and_return(@user_mock) @knife.config[:file] = '/tmp/a_file' filehandle = StringIO.new expect(File).to receive(:open).with('/tmp/a_file', 'w').and_yield(filehandle) @knife.run expect(filehandle.string).to eq("private_key") end end chef-12.3.0/spec/unit/knife/node_show_spec.rb0000644000004100000410000000413012520074675021071 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeShow do let(:node) do node = Chef::Node.new() node.name("adam") node.run_list = ['role[base]'] node end let(:knife) do knife = Chef::Knife::NodeShow.new knife.name_args = [ "adam" ] knife end before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" end describe "run" do it "should load the node" do expect(Chef::Node).to receive(:load).with("adam").and_return(node) allow(knife).to receive(:output).and_return(true) knife.run end it "should pretty print the node, formatted for display" do knife.config[:format] = nil stdout = StringIO.new allow(knife.ui).to receive(:stdout).and_return(stdout) allow(Chef::Node).to receive(:load).and_return(node) knife.run expect(stdout.string).to eql("Node Name: adam\nEnvironment: _default\nFQDN: \nIP: \nRun List: \nRoles: \nRecipes: \nPlatform: \nTags: \n") end it "should pretty print json" do knife.config[:format] = 'json' stdout = StringIO.new allow(knife.ui).to receive(:stdout).and_return(stdout) expect(Chef::Node).to receive(:load).with('adam').and_return(node) knife.run expect(stdout.string).to eql("{\n \"name\": \"adam\",\n \"chef_environment\": \"_default\",\n \"run_list\": [\n\n]\n,\n \"normal\": {\n\n }\n}\n") end end end chef-12.3.0/spec/unit/knife/role_list_spec.rb0000644000004100000410000000324012520074675021101 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::RoleList do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleList.new allow(@knife).to receive(:output).and_return(true) @list = { "foo" => "http://example.com/foo", "bar" => "http://example.com/foo" } allow(Chef::Role).to receive(:list).and_return(@list) end describe "run" do it "should list the roles" do expect(Chef::Role).to receive(:list).and_return(@list) @knife.run end it "should pretty print the list" do expect(Chef::Role).to receive(:list).and_return(@list) expect(@knife).to receive(:output).with([ "bar", "foo" ]) @knife.run end describe "with -w or --with-uri" do it "should pretty print the hash" do @knife.config[:with_uri] = true expect(Chef::Role).to receive(:list).and_return(@list) expect(@knife).to receive(:output).with(@list) @knife.run end end end end chef-12.3.0/spec/unit/knife/environment_show_spec.rb0000644000004100000410000000340212520074675022511 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::EnvironmentShow do before(:each) do @knife = Chef::Knife::EnvironmentShow.new allow(@knife).to receive(:msg).and_return true allow(@knife).to receive(:output).and_return true allow(@knife).to receive(:show_usage).and_return true @knife.name_args = [ "production" ] @environment = Chef::Environment.new @environment.name("production") @environment.description("Look at me!") allow(Chef::Environment).to receive(:load).and_return @environment end it "should load the environment" do expect(Chef::Environment).to receive(:load).with("production") @knife.run end it "should pretty print the environment, formatted for display" do expect(@knife).to receive(:format_for_display).with(@environment) expect(@knife).to receive(:output) @knife.run end it "should show usage and exit when no environment name is provided" do @knife.name_args = [] expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end end chef-12.3.0/spec/unit/knife/data_bag_from_file_spec.rb0000644000004100000410000001522412520074675022656 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright (c) 2010 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/data_bag_item' require 'chef/encrypted_data_bag_item' require 'tempfile' Chef::Knife::DataBagFromFile.load_deps describe Chef::Knife::DataBagFromFile do before :each do allow(Chef::Platform).to receive(:windows?) { false } Chef::Config[:node_name] = "webmonkey.example.com" FileUtils.mkdir_p([db_folder, db_folder2]) db_file.write(Chef::JSONCompat.to_json(plain_data)) db_file.flush allow(knife).to receive(:config).and_return(config) allow(Chef::Knife::Core::ObjectLoader).to receive(:new).and_return(loader) end # We have to explicitly clean up Tempfile on Windows because it said so. after :each do db_file.close db_file2.close db_file3.close FileUtils.rm_rf(db_folder) FileUtils.rm_rf(db_folder2) FileUtils.remove_entry_secure tmp_dir end let(:knife) do k = Chef::Knife::DataBagFromFile.new allow(k).to receive(:rest).and_return(rest) allow(k.ui).to receive(:stdout).and_return(stdout) k end let(:tmp_dir) { Dir.mktmpdir } let(:db_folder) { File.join(tmp_dir, data_bags_path, bag_name) } let(:db_file) { Tempfile.new(["data_bag_from_file_test", ".json"], db_folder) } let(:db_file2) { Tempfile.new(["data_bag_from_file_test2", ".json"], db_folder) } let(:db_folder2) { File.join(tmp_dir, data_bags_path, bag_name2) } let(:db_file3) { Tempfile.new(["data_bag_from_file_test3", ".json"], db_folder2) } def new_bag_expects(b = bag_name, d = plain_data) data_bag = double expect(data_bag).to receive(:data_bag).with(b) expect(data_bag).to receive(:raw_data=).with(d) expect(data_bag).to receive(:save) expect(data_bag).to receive(:data_bag) expect(data_bag).to receive(:id) data_bag end let(:loader) { double("Knife::Core::ObjectLoader") } let(:data_bags_path) { "data_bags" } let(:plain_data) { { "id" => "item_name", "greeting" => "hello", "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true }} } } let(:enc_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(plain_data, secret) } let(:rest) { double("Chef::REST") } let(:stdout) { StringIO.new } let(:bag_name) { "sudoing_admins" } let(:bag_name2) { "sudoing_admins2" } let(:item_name) { "ME" } let(:secret) { "abc123SECRET" } let(:config) { {} } it "loads from a file and saves" do knife.name_args = [bag_name, db_file.path] expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file.path).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).and_return(new_bag_expects) knife.run end it "loads all from multiple files and saves" do knife.name_args = [ bag_name, db_file.path, db_file2.path ] expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file.path).and_return(plain_data) expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file2.path).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).twice.and_return(new_bag_expects, new_bag_expects) knife.run end it "loads all from a folder and saves" do knife.name_args = [ bag_name, db_folder ] expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file.path).and_return(plain_data) expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file2.path).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).twice.and_return(new_bag_expects, new_bag_expects) knife.run end describe "loading all data bags" do it "loads all data bags when -a or --all options is provided" do knife.name_args = [] config[:all] = true expect(loader).to receive(:find_all_object_dirs).with("./#{data_bags_path}").and_return([bag_name, bag_name2]) expect(loader).to receive(:find_all_objects).with("./#{data_bags_path}/#{bag_name}").and_return([File.basename(db_file.path), File.basename(db_file2.path)]) expect(loader).to receive(:find_all_objects).with("./#{data_bags_path}/#{bag_name2}").and_return([File.basename(db_file3.path)]) expect(loader).to receive(:load_from).with(data_bags_path, bag_name, File.basename(db_file.path)).and_return(plain_data) expect(loader).to receive(:load_from).with(data_bags_path, bag_name, File.basename(db_file2.path)).and_return(plain_data) expect(loader).to receive(:load_from).with(data_bags_path, bag_name2, File.basename(db_file3.path)).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).exactly(3).times.and_return(new_bag_expects, new_bag_expects, new_bag_expects(bag_name2)) knife.run end it "loads all data bags items when -a or --all options is provided" do knife.name_args = [bag_name2] config[:all] = true expect(loader).to receive(:find_all_objects).with("./#{data_bags_path}/#{bag_name2}").and_return([File.basename(db_file3.path)]) expect(loader).to receive(:load_from).with(data_bags_path, bag_name2, File.basename(db_file3.path)).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).and_return(new_bag_expects(bag_name2)) knife.run end end describe "encrypted data bag items" do before(:each) do expect(knife).to receive(:encryption_secret_provided?).and_return(true) expect(knife).to receive(:read_secret).and_return(secret) expect(Chef::EncryptedDataBagItem).to receive(:encrypt_data_bag_item).with(plain_data, secret).and_return(enc_data) end it "encrypts values when given --secret" do knife.name_args = [bag_name, db_file.path] expect(loader).to receive(:load_from).with(data_bags_path, bag_name, db_file.path).and_return(plain_data) expect(Chef::DataBagItem).to receive(:new).and_return(new_bag_expects(bag_name, enc_data)) knife.run end end describe "command line parsing" do it "prints help if given no arguments" do knife.name_args = [bag_name] expect {knife.run}.to exit_with_code(1) expect(stdout.string).to start_with("knife data bag from file BAG FILE|FOLDER [FILE|FOLDER..] (options)") end end end chef-12.3.0/spec/unit/knife/client_create_spec.rb0000644000004100000410000000717612520074675021722 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' Chef::Knife::ClientCreate.load_deps describe Chef::Knife::ClientCreate do let(:stderr) { StringIO.new } let(:default_client_hash) do { "name" => "adam", "validator" => false, "admin" => false } end let(:client) do c = double("Chef::ApiClient") allow(c).to receive(:save).and_return({"private_key" => ""}) allow(c).to receive(:to_s).and_return("client[adam]") c end let(:knife) do k = Chef::Knife::ClientCreate.new k.name_args = [ "adam" ] k.ui.config[:disable_editing] = true allow(k.ui).to receive(:stderr).and_return(stderr) allow(k.ui).to receive(:stdout).and_return(stderr) k end before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" end describe "run" do it "should create and save the ApiClient" do expect(Chef::ApiClient).to receive(:from_hash).and_return(client) expect(client).to receive(:save) knife.run end it "should print a message upon creation" do expect(Chef::ApiClient).to receive(:from_hash).and_return(client) expect(client).to receive(:save) knife.run expect(stderr.string).to match /Created client.*adam/i end it "should set the Client name" do expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("name" => "adam")).and_return(client) knife.run end it "by default it is not an admin" do expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("admin" => false)).and_return(client) knife.run end it "by default it is not a validator" do expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("validator" => false)).and_return(client) knife.run end it "should allow you to edit the data" do expect(knife).to receive(:edit_hash).with(default_client_hash).and_return(default_client_hash) allow(Chef::ApiClient).to receive(:from_hash).and_return(client) knife.run end describe "with -f or --file" do it "should write the private key to a file" do knife.config[:file] = "/tmp/monkeypants" allow_any_instance_of(Chef::ApiClient).to receive(:save).and_return({ 'private_key' => "woot" }) filehandle = double("Filehandle") expect(filehandle).to receive(:print).with('woot') expect(File).to receive(:open).with("/tmp/monkeypants", "w").and_yield(filehandle) knife.run end end describe "with -a or --admin" do it "should create an admin client" do knife.config[:admin] = true expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("admin" => true)).and_return(client) knife.run end end describe "with --validator" do it "should create an validator client" do knife.config[:validator] = true expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("validator" => true)).and_return(client) knife.run end end end end chef-12.3.0/spec/unit/knife/node_bulk_delete_spec.rb0000644000004100000410000000601312520074675022372 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeBulkDelete do before(:each) do Chef::Log.logger = Logger.new(StringIO.new) Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeBulkDelete.new @knife.name_args = ["."] @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:confirm).and_return(true) @nodes = Hash.new %w{adam brent jacob}.each do |node_name| @nodes[node_name] = "http://localhost:4000/nodes/#{node_name}" end end describe "when creating the list of nodes" do it "fetches the node list" do expected = @nodes.inject({}) do |inflatedish, (name, uri)| inflatedish[name] = Chef::Node.new.tap {|n| n.name(name)} inflatedish end expect(Chef::Node).to receive(:list).and_return(@nodes) # I hate not having == defined for anything :( actual = @knife.all_nodes expect(actual.keys).to match_array(expected.keys) expect(actual.values.map {|n| n.name }).to match_array(%w[adam brent jacob]) end end describe "run" do before do @inflatedish_list = @nodes.keys.inject({}) do |nodes_by_name, name| node = Chef::Node.new() node.name(name) allow(node).to receive(:destroy).and_return(true) nodes_by_name[name] = node nodes_by_name end allow(@knife).to receive(:all_nodes).and_return(@inflatedish_list) end it "should print the nodes you are about to delete" do @knife.run expect(@stdout.string).to match(/#{@knife.ui.list(@nodes.keys.sort, :columns_down)}/) end it "should confirm you really want to delete them" do expect(@knife.ui).to receive(:confirm) @knife.run end it "should delete each node" do @inflatedish_list.each_value do |n| expect(n).to receive(:destroy) end @knife.run end it "should only delete nodes that match the regex" do @knife.name_args = ['adam'] expect(@inflatedish_list['adam']).to receive(:destroy) expect(@inflatedish_list['brent']).not_to receive(:destroy) expect(@inflatedish_list['jacob']).not_to receive(:destroy) @knife.run end it "should exit if the regex is not provided" do @knife.name_args = [] expect { @knife.run }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/role_env_run_list_replace_spec.rb0000644000004100000410000000776612520074675024351 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleEnvRunListReplace do before(:each) do Chef::Config[:role_name] = "will" Chef::Config[:env_name] = "QA" @setup = Chef::Knife::RoleEnvRunListAdd.new @setup.name_args = [ "will", "QA", "role[monkey]", "role[dude]", "role[fixer]" ] @knife = Chef::Knife::RoleEnvRunListReplace.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "QA", "role[dude]", "role[person]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list_for('QA')[1]).not_to eq('role[dude]') expect(@role.run_list_for('QA')[1]).to eq('role[person]') expect(@role.run_list[0]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "run with a list of roles and recipes" do it "should replace the items from the run list" do @setup.name_args = [ "will", "QA", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @setup.name_args = [ "will", "PRD", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will', 'QA', 'role[monkey]', 'role[gibbon]' ] @knife.run @knife.name_args = [ 'will', 'QA', 'recipe[duck::type]', 'recipe[duck::mallard]' ] @knife.run expect(@role.run_list_for('QA')).not_to include('role[monkey]') expect(@role.run_list_for('QA')).not_to include('recipe[duck::type]') expect(@role.run_list_for('QA')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('QA')[1]).to eq('role[gibbon]') expect(@role.run_list_for('QA')[2]).to eq('recipe[duck::mallard]') expect(@role.run_list_for('QA')[3]).to eq('role[person]') expect(@role.run_list_for('QA')[4]).to eq('role[bird]') expect(@role.run_list_for('QA')[5]).to eq('role[town]') expect(@role.run_list_for('PRD')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('PRD')[1]).to eq('role[monkey]') expect(@role.run_list_for('PRD')[2]).to eq('recipe[duck::type]') expect(@role.run_list_for('PRD')[3]).to eq('role[person]') expect(@role.run_list_for('PRD')[4]).to eq('role[bird]') expect(@role.run_list_for('PRD')[5]).to eq('role[town]') expect(@role.run_list[0]).to be_nil end end end end chef-12.3.0/spec/unit/knife/cookbook_metadata_from_file_spec.rb0000644000004100000410000000411612520074675024600 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Matthew Kent () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2010 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::CookbookMetadataFromFile do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @src = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.rb")) @tgt = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.json")) @knife = Chef::Knife::CookbookMetadataFromFile.new @knife.name_args = [ @src ] allow(@knife).to receive(:to_json_pretty).and_return(true) @md = Chef::Cookbook::Metadata.new allow(Chef::Cookbook::Metadata).to receive(:new).and_return(@md) allow($stdout).to receive(:write) end after do if File.exists?(@tgt) File.unlink(@tgt) end end describe "run" do it "should determine cookbook name from path" do expect(@md).to receive(:name).with(no_args) expect(@md).to receive(:name).with("quick_start") @knife.run end it "should load the metadata source" do expect(@md).to receive(:from_file).with(@src) @knife.run end it "should write out the metadata to the correct location" do expect(File).to receive(:open).with(@tgt, "w") @knife.run end it "should generate json from the metadata" do expect(Chef::JSONCompat).to receive(:to_json_pretty).with(@md) @knife.run end end end chef-12.3.0/spec/unit/knife/role_delete_spec.rb0000644000004100000410000000376312520074675021402 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::RoleDelete do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleDelete.new @knife.config = { :print_after => nil } @knife.name_args = [ "adam" ] allow(@knife).to receive(:output).and_return(true) allow(@knife).to receive(:confirm).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:destroy).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should confirm that you want to delete" do expect(@knife).to receive(:confirm) @knife.run end it "should load the Role" do expect(Chef::Role).to receive(:load).with("adam").and_return(@role) @knife.run end it "should delete the Role" do expect(@role).to receive(:destroy).and_return(@role) @knife.run end it "should not print the Role" do expect(@knife).not_to receive(:output) @knife.run end describe "with -p or --print-after" do it "should pretty print the Role, formatted for display" do @knife.config[:print_after] = true expect(@knife).to receive(:output) @knife.run end end end end chef-12.3.0/spec/unit/knife/cookbook_delete_spec.rb0000644000004100000410000002147312520074675022245 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::CookbookDelete do before(:each) do @knife = Chef::Knife::CookbookDelete.new @knife.name_args = ['foobar'] @knife.cookbook_name = 'foobar' @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) @stderr = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe 'run' do it 'should print usage and exit when a cookbook name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end describe 'when specifying a cookbook name' do it 'should delete the cookbook without a specific version' do expect(@knife).to receive(:delete_without_explicit_version) @knife.run end describe 'and a version' do it 'should delete the specific version of the cookbook' do @knife.name_args << '1.0.0' expect(@knife).to receive(:delete_explicit_version) @knife.run end end describe 'with -a or --all' do it 'should delete all versions of the cookbook' do @knife.config[:all] = true expect(@knife).to receive(:delete_all_versions) @knife.run end end describe 'with -p or --purge' do it 'should prompt to purge the files' do @knife.config[:purge] = true expect(@knife).to receive(:confirm). with(/.+Are you sure you want to purge files.+/) expect(@knife).to receive(:delete_without_explicit_version) @knife.run end end end end describe 'delete_explicit_version' do it 'should delete the specific cookbook version' do @knife.cookbook_name = 'foobar' @knife.version = '1.0.0' expect(@knife).to receive(:delete_object).with(Chef::CookbookVersion, 'foobar version 1.0.0', 'cookbook').and_yield() expect(@knife).to receive(:delete_request).with('cookbooks/foobar/1.0.0') @knife.delete_explicit_version end end describe 'delete_all_versions' do it 'should prompt to delete all versions of the cookbook' do @knife.cookbook_name = 'foobar' expect(@knife).to receive(:confirm).with('Do you really want to delete all versions of foobar') expect(@knife).to receive(:delete_all_without_confirmation) @knife.delete_all_versions end end describe 'delete_all_without_confirmation' do it 'should delete all versions without confirmation' do versions = ['1.0.0', '1.1.0'] expect(@knife).to receive(:available_versions).and_return(versions) versions.each do |v| expect(@knife).to receive(:delete_version_without_confirmation).with(v) end @knife.delete_all_without_confirmation end end describe 'delete_without_explicit_version' do it 'should exit if there are no available versions' do expect(@knife).to receive(:available_versions).and_return(nil) expect { @knife.delete_without_explicit_version }.to raise_error(SystemExit) end it 'should delete the version if only one is found' do expect(@knife).to receive(:available_versions).at_least(:once).and_return(['1.0.0']) expect(@knife).to receive(:delete_explicit_version) @knife.delete_without_explicit_version end it 'should ask which version(s) to delete if multiple are found' do expect(@knife).to receive(:available_versions).at_least(:once).and_return(['1.0.0', '1.1.0']) expect(@knife).to receive(:ask_which_versions_to_delete).and_return(['1.0.0', '1.1.0']) expect(@knife).to receive(:delete_versions_without_confirmation).with(['1.0.0', '1.1.0']) @knife.delete_without_explicit_version end end describe 'available_versions' do before(:each) do @rest_mock = double('rest') expect(@knife).to receive(:rest).and_return(@rest_mock) @cookbook_data = { 'foobar' => { 'versions' => [{'version' => '1.0.0'}, {'version' => '1.1.0'}, {'version' => '2.0.0'} ]} } end it 'should return the list of versions of the cookbook' do expect(@rest_mock).to receive(:get_rest).with('cookbooks/foobar').and_return(@cookbook_data) expect(@knife.available_versions).to eq(['1.0.0', '1.1.0', '2.0.0']) end it 'should raise if an error other than HTTP 404 is returned' do exception = Net::HTTPServerException.new('500 Internal Server Error', '500') expect(@rest_mock).to receive(:get_rest).and_raise(exception) expect { @knife.available_versions }.to raise_error Net::HTTPServerException end describe "if the cookbook can't be found" do before(:each) do expect(@rest_mock).to receive(:get_rest). and_raise(Net::HTTPServerException.new('404 Not Found', '404')) end it 'should print an error' do @knife.available_versions expect(@stderr.string).to match /error.+cannot find a cookbook named foobar/i end it 'should return nil' do expect(@knife.available_versions).to eq(nil) end end end describe 'ask_which_version_to_delete' do before(:each) do allow(@knife).to receive(:available_versions).and_return(['1.0.0', '1.1.0', '2.0.0']) end it 'should prompt the user to select a version' do prompt = /Which version\(s\) do you want to delete\?.+1\. foobar 1\.0\.0.+2\. foobar 1\.1\.0.+3\. foobar 2\.0\.0.+4\. All versions.+/m expect(@knife).to receive(:ask_question).with(prompt).and_return('1') @knife.ask_which_versions_to_delete end it "should print an error and exit if a version wasn't specified" do expect(@knife).to receive(:ask_question).and_return('') expect(@knife.ui).to receive(:error).with(/no versions specified/i) expect { @knife.ask_which_versions_to_delete }.to raise_error(SystemExit) end it 'should print an error if an invalid choice was selected' do expect(@knife).to receive(:ask_question).and_return('100') expect(@knife.ui).to receive(:error).with(/100 is not a valid choice/i) @knife.ask_which_versions_to_delete end it 'should return the selected versions' do expect(@knife).to receive(:ask_question).and_return('1, 3') expect(@knife.ask_which_versions_to_delete).to eq(['1.0.0', '2.0.0']) end it "should return all of the versions if 'all' was selected" do expect(@knife).to receive(:ask_question).and_return('4') expect(@knife.ask_which_versions_to_delete).to eq([:all]) end end describe 'delete_version_without_confirmation' do it 'should delete the cookbook version' do expect(@knife).to receive(:delete_request).with('cookbooks/foobar/1.0.0') @knife.delete_version_without_confirmation('1.0.0') end it 'should output that the cookbook was deleted' do allow(@knife).to receive(:delete_request) @knife.delete_version_without_confirmation('1.0.0') expect(@stderr.string).to match /deleted cookbook\[foobar\]\[1.0.0\]/im end describe 'with --print-after' do it 'should display the cookbook data' do object = '' @knife.config[:print_after] = true allow(@knife).to receive(:delete_request).and_return(object) expect(@knife).to receive(:format_for_display).with(object) @knife.delete_version_without_confirmation('1.0.0') end end end describe 'delete_versions_without_confirmation' do it 'should delete each version without confirmation' do versions = ['1.0.0', '1.1.0'] versions.each do |v| expect(@knife).to receive(:delete_version_without_confirmation).with(v) end @knife.delete_versions_without_confirmation(versions) end describe 'with -a or --all' do it 'should delete all versions without confirmation' do versions = [:all] expect(@knife).to receive(:delete_all_without_confirmation) @knife.delete_versions_without_confirmation(versions) end end end end chef-12.3.0/spec/unit/knife/bootstrap_spec.rb0000644000004100000410000005575512520074675021144 0ustar www-datawww-data# # Author:: Ian Meyer () # Copyright:: Copyright (c) 2010 Ian Meyer # 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::Bootstrap.load_deps require 'net/ssh' describe Chef::Knife::Bootstrap do before do allow(Chef::Platform).to receive(:windows?) { false } end let(:knife) do Chef::Log.logger = Logger.new(StringIO.new) Chef::Config[:knife][:bootstrap_template] = bootstrap_template unless bootstrap_template.nil? k = Chef::Knife::Bootstrap.new(bootstrap_cli_options) k.merge_configs allow(k.ui).to receive(:stderr).and_return(stderr) allow(k).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false) k end let(:stderr) { StringIO.new } let(:bootstrap_template) { nil } let(:bootstrap_cli_options) { [ ] } it "should use chef-full as default template" do expect(knife.bootstrap_template).to be_a_kind_of(String) expect(File.basename(knife.bootstrap_template)).to eq("chef-full") end context "with --bootstrap-vault-item" do let(:bootstrap_cli_options) { [ "--bootstrap-vault-item", "vault1:item1", "--bootstrap-vault-item", "vault1:item2", "--bootstrap-vault-item", "vault2:item1" ] } it "sets the knife config cli option correctly" do expect(knife.config[:bootstrap_vault_item]).to eq({"vault1"=>["item1", "item2"], "vault2"=>["item1"]}) end end context "with :distro and :bootstrap_template cli options" do let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "--distro", "other-template" ] } it "should select bootstrap template" do expect(File.basename(knife.bootstrap_template)).to eq("my-template") end end context "with :distro and :template_file cli options" do let(:bootstrap_cli_options) { [ "--distro", "my-template", "--template-file", "other-template" ] } it "should select bootstrap template" do expect(File.basename(knife.bootstrap_template)).to eq("other-template") end end context "with :bootstrap_template and :template_file cli options" do let(:bootstrap_cli_options) { [ "--bootstrap-template", "my-template", "--template-file", "other-template" ] } it "should select bootstrap template" do expect(File.basename(knife.bootstrap_template)).to eq("my-template") end end context "when finding templates" do context "when :bootstrap_template config is set to a file" do context "that doesn't exist" do let(:bootstrap_template) { "/opt/blah/not/exists/template.erb" } it "raises an error" do expect { knife.find_template }.to raise_error end end context "that exists" do let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) } it "loads the given file as the template" do expect(Chef::Log).to receive(:debug) expect(knife.find_template).to eq(File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb"))) end end end context "when :bootstrap_template config is set to a template name" do let(:bootstrap_template) { "example" } let(:builtin_template_path) { File.expand_path(File.join(File.dirname(__FILE__), '../../../lib/chef/knife/bootstrap/templates', "example.erb"))} let(:chef_config_dir_template_path) { "/knife/chef/config/bootstrap/example.erb" } let(:env_home_template_path) { "/env/home/.chef/bootstrap/example.erb" } let(:gem_files_template_path) { "/Users/schisamo/.rvm/gems/ruby-1.9.2-p180@chef-0.10/gems/knife-windows-0.5.4/lib/chef/knife/bootstrap/fake-bootstrap-template.erb" } def configure_chef_config_dir allow(Chef::Knife).to receive(:chef_config_dir).and_return("/knife/chef/config") end def configure_env_home allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path) end def configure_gem_files allow(Gem).to receive(:find_files).and_return([ gem_files_template_path ]) end before(:each) do expect(File).to receive(:exists?).with(bootstrap_template).and_return(false) end context "when file is available everywhere" do before do configure_chef_config_dir configure_env_home configure_gem_files expect(File).to receive(:exists?).with(builtin_template_path).and_return(true) end it "should load the template from built-in templates" do expect(knife.find_template).to eq(builtin_template_path) end end context "when file is available in chef_config_dir" do before do configure_chef_config_dir configure_env_home configure_gem_files expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(true) it "should load the template from chef_config_dir" do knife.find_template.should eq(chef_config_dir_template_path) end end end context "when file is available in home directory" do before do configure_chef_config_dir configure_env_home configure_gem_files expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false) expect(File).to receive(:exists?).with(env_home_template_path).and_return(true) end it "should load the template from chef_config_dir" do expect(knife.find_template).to eq(env_home_template_path) end end context "when file is available in Gem files" do before do configure_chef_config_dir configure_env_home configure_gem_files expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false) expect(File).to receive(:exists?).with(env_home_template_path).and_return(false) expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true) end it "should load the template from Gem files" do expect(knife.find_template).to eq(gem_files_template_path) end end context "when file is available in Gem files and home dir doesn't exist" do before do configure_chef_config_dir configure_gem_files allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil) expect(File).to receive(:exists?).with(builtin_template_path).and_return(false) expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false) expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true) end it "should load the template from Gem files" do expect(knife.find_template).to eq(gem_files_template_path) end end end end ["-d", "--distro", "-t", "--bootstrap-template", "--template-file"].each do |t| context "when #{t} option is given in the command line" do it "sets the knife :bootstrap_template config" do knife.parse_options([t,"blahblah"]) knife.merge_configs expect(knife.bootstrap_template).to eq("blahblah") end end end context "with run_list template" do let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test.erb")) } it "should return an empty run_list" do expect(knife.render_template).to eq('{"run_list":[]}') end it "should have role[base] in the run_list" do knife.parse_options(["-r","role[base]"]) knife.merge_configs expect(knife.render_template).to eq('{"run_list":["role[base]"]}') end it "should have role[base] and recipe[cupcakes] in the run_list" do knife.parse_options(["-r", "role[base],recipe[cupcakes]"]) knife.merge_configs expect(knife.render_template).to eq('{"run_list":["role[base]","recipe[cupcakes]"]}') end it "should have foo => {bar => baz} in the first_boot" do knife.parse_options(["-j", '{"foo":{"bar":"baz"}}']) knife.merge_configs expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}') actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template) expect(actual_hash).to eq(expected_hash) end end context "with hints template" do let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "test-hints.erb")) } it "should create a hint file when told to" do knife.parse_options(["--hint", "openstack"]) knife.merge_configs expect(knife.render_template).to match /\/etc\/chef\/ohai\/hints\/openstack.json/ end it "should populate a hint file with JSON when given a file to read" do allow(::File).to receive(:read).and_return('{ "foo" : "bar" }') knife.parse_options(["--hint", "openstack=hints/openstack.json"]) knife.merge_configs expect(knife.render_template).to match /\{\"foo\":\"bar\"\}/ end end describe "specifying no_proxy with various entries" do subject(:knife) do k = described_class.new Chef::Config[:knife][:bootstrap_template] = template_file k.parse_options(options) k.merge_configs k end let(:options){ ["--bootstrap-no-proxy", setting, "-s", "foo"] } let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) } let(:rendered_template) do knife.render_template end context "via --bootstrap-no-proxy" do let(:setting) { "api.opscode.com" } it "renders the client.rb with a single FQDN no_proxy entry" do expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com".*}) end end context "via --bootstrap-no-proxy multiple" do let(:setting) { "api.opscode.com,172.16.10.*" } it "renders the client.rb with comma-separated FQDN and wildcard IP address no_proxy entries" do expect(rendered_template).to match(%r{.*no_proxy\s*"api.opscode.com,172.16.10.\*".*}) end end context "via --ssl-verify-mode none" do let(:options) { ["--node-ssl-verify-mode", "none"] } it "renders the client.rb with ssl_verify_mode set to :verify_none" do expect(rendered_template).to match(/ssl_verify_mode :verify_none/) end end context "via --node-ssl-verify-mode peer" do let(:options) { ["--node-ssl-verify-mode", "peer"] } it "renders the client.rb with ssl_verify_mode set to :verify_peer" do expect(rendered_template).to match(/ssl_verify_mode :verify_peer/) end end context "via --node-ssl-verify-mode all" do let(:options) { ["--node-ssl-verify-mode", "all"] } it "raises error" do expect{ rendered_template }.to raise_error end end context "via --node-verify-api-cert" do let(:options) { ["--node-verify-api-cert"] } it "renders the client.rb with verify_api_cert set to true" do expect(rendered_template).to match(/verify_api_cert true/) end end context "via --no-node-verify-api-cert" do let(:options) { ["--no-node-verify-api-cert"] } it "renders the client.rb with verify_api_cert set to false" do expect(rendered_template).to match(/verify_api_cert false/) end end end describe "specifying the encrypted data bag secret key" do let(:secret) { "supersekret" } let(:options) { [] } let(:bootstrap_template) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "secret.erb")) } let(:rendered_template) do knife.parse_options(options) knife.merge_configs knife.render_template end it "creates a secret file" do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true) expect(knife).to receive(:read_secret).and_return(secret) expect(rendered_template).to match(%r{#{secret}}) end it "renders the client.rb with an encrypted_data_bag_secret entry" do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true) expect(knife).to receive(:read_secret).and_return(secret) expect(rendered_template).to match(%r{encrypted_data_bag_secret\s*"/etc/chef/encrypted_data_bag_secret"}) end end describe "when transferring trusted certificates" do let(:trusted_certs_dir) { Chef::Util::PathHelper.cleanpath(File.join(File.dirname(__FILE__), '../../data/trusted_certs')) } let(:rendered_template) do knife.merge_configs knife.render_template end before do Chef::Config[:trusted_certs_dir] = trusted_certs_dir allow(IO).to receive(:read).and_call_original allow(IO).to receive(:read).with(File.expand_path(Chef::Config[:validation_key])).and_return("") end def certificates Dir[File.join(trusted_certs_dir, "*.{crt,pem}")] end it "creates /etc/chef/trusted_certs" do expect(rendered_template).to match(%r{mkdir -p /etc/chef/trusted_certs}) end it "copies the certificates in the directory" do certificates.each do |cert| expect(IO).to receive(:read).with(File.expand_path(cert)) end certificates.each do |cert| expect(rendered_template).to match(%r{cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'}) end end it "doesn't create /etc/chef/trusted_certs if :trusted_certs_dir is empty" do expect(Dir).to receive(:glob).with(File.join(trusted_certs_dir, "*.{crt,pem}")).and_return([]) expect(rendered_template).not_to match(%r{mkdir -p /etc/chef/trusted_certs}) end end describe "when configuring the underlying knife ssh command" do context "from the command line" do let(:knife_ssh) do knife.name_args = ["foo.example.com"] knife.config[:ssh_user] = "rooty" knife.config[:ssh_port] = "4001" knife.config[:ssh_password] = "open_sesame" Chef::Config[:knife][:ssh_user] = nil Chef::Config[:knife][:ssh_port] = nil knife.config[:forward_agent] = true knife.config[:identity_file] = "~/.ssh/me.rsa" allow(knife).to receive(:render_template).and_return("") knife.knife_ssh end it "configures the hostname" do expect(knife_ssh.name_args.first).to eq("foo.example.com") end it "configures the ssh user" do expect(knife_ssh.config[:ssh_user]).to eq('rooty') end it "configures the ssh password" do expect(knife_ssh.config[:ssh_password]).to eq('open_sesame') end it "configures the ssh port" do expect(knife_ssh.config[:ssh_port]).to eq('4001') end it "configures the ssh agent forwarding" do expect(knife_ssh.config[:forward_agent]).to eq(true) end it "configures the ssh identity file" do expect(knife_ssh.config[:identity_file]).to eq('~/.ssh/me.rsa') end end context "validating use_sudo_password" do before do knife.config[:ssh_password] = "password" allow(knife).to receive(:render_template).and_return("") end it "use_sudo_password contains description and long params for help" do expect(knife.options).to have_key(:use_sudo_password) \ and expect(knife.options[:use_sudo_password][:description].to_s).not_to eq('')\ and expect(knife.options[:use_sudo_password][:long].to_s).not_to eq('') end it "uses the password from --ssh-password for sudo when --use-sudo-password is set" do knife.config[:use_sudo] = true knife.config[:use_sudo_password] = true expect(knife.ssh_command).to include("echo \'#{knife.config[:ssh_password]}\' | sudo -S") end it "should not honor --use-sudo-password when --use-sudo is not set" do knife.config[:use_sudo] = false knife.config[:use_sudo_password] = true expect(knife.ssh_command).not_to include("echo #{knife.config[:ssh_password]} | sudo -S") end end context "from the knife config file" do let(:knife_ssh) do knife.name_args = ["config.example.com"] Chef::Config[:knife][:ssh_user] = "curiosity" Chef::Config[:knife][:ssh_port] = "2430" Chef::Config[:knife][:forward_agent] = true Chef::Config[:knife][:identity_file] = "~/.ssh/you.rsa" Chef::Config[:knife][:ssh_gateway] = "towel.blinkenlights.nl" Chef::Config[:knife][:host_key_verify] = true allow(knife).to receive(:render_template).and_return("") knife.config = {} knife.merge_configs knife.knife_ssh end it "configures the ssh user" do expect(knife_ssh.config[:ssh_user]).to eq('curiosity') end it "configures the ssh port" do expect(knife_ssh.config[:ssh_port]).to eq('2430') end it "configures the ssh agent forwarding" do expect(knife_ssh.config[:forward_agent]).to eq(true) end it "configures the ssh identity file" do expect(knife_ssh.config[:identity_file]).to eq('~/.ssh/you.rsa') end it "configures the ssh gateway" do expect(knife_ssh.config[:ssh_gateway]).to eq('towel.blinkenlights.nl') end it "configures the host key verify mode" do expect(knife_ssh.config[:host_key_verify]).to eq(true) end end describe "when falling back to password auth when host key auth fails" do let(:knife_ssh_with_password_auth) do knife.name_args = ["foo.example.com"] knife.config[:ssh_user] = "rooty" knife.config[:identity_file] = "~/.ssh/me.rsa" allow(knife).to receive(:render_template).and_return("") k = knife.knife_ssh allow(k).to receive(:get_password).and_return('typed_in_password') allow(knife).to receive(:knife_ssh).and_return(k) knife.knife_ssh_with_password_auth end it "prompts the user for a password " do expect(knife_ssh_with_password_auth.config[:ssh_password]).to eq('typed_in_password') end it "configures knife not to use the identity file that didn't work previously" do expect(knife_ssh_with_password_auth.config[:identity_file]).to be_nil end end end it "verifies that a server to bootstrap was given as a command line arg" do knife.name_args = nil expect { knife.run }.to raise_error(SystemExit) expect(stderr.string).to match /ERROR:.+FQDN or ip/ end describe "when running the bootstrap" do let(:knife_ssh) do knife.name_args = ["foo.example.com"] knife.config[:ssh_user] = "rooty" knife.config[:identity_file] = "~/.ssh/me.rsa" allow(knife).to receive(:render_template).and_return("") knife_ssh = knife.knife_ssh allow(knife).to receive(:knife_ssh).and_return(knife_ssh) knife_ssh end context "when running with a configured and present validation key" do before do # this tests runs the old code path where we have a validation key, so we need to pass that check allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true) end it "configures the underlying ssh command and then runs it" do expect(knife_ssh).to receive(:run) knife.run end it "falls back to password based auth when auth fails the first time" do allow(knife).to receive(:puts) fallback_knife_ssh = knife_ssh.dup expect(knife_ssh).to receive(:run).and_raise(Net::SSH::AuthenticationFailed.new("no ssh for you")) allow(knife).to receive(:knife_ssh_with_password_auth).and_return(fallback_knife_ssh) expect(fallback_knife_ssh).to receive(:run) knife.run end it "raises the exception if config[:ssh_password] is set and an authentication exception is raised" do knife.config[:ssh_password] = "password" expect(knife_ssh).to receive(:run).and_raise(Net::SSH::AuthenticationFailed) expect { knife.run }.to raise_error(Net::SSH::AuthenticationFailed) end it "creates the client and adds chef-vault items if vault_list is set" do knife.config[:bootstrap_vault_file] = "/not/our/responsibility/to/check/if/this/exists" expect(knife_ssh).to receive(:run) expect(knife.client_builder).to receive(:run) expect(knife.chef_vault_handler).to receive(:run).with(node_name: knife.config[:chef_node_name]) knife.run end it "creates the client and adds chef-vault items if vault_items is set" do knife.config[:bootstrap_vault_json] = '{ "vault" => "item" }' expect(knife_ssh).to receive(:run) expect(knife.client_builder).to receive(:run) expect(knife.chef_vault_handler).to receive(:run).with(node_name: knife.config[:chef_node_name]) knife.run end it "does old-style validation without creating a client key if vault_list+vault_items is not set" do expect(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true) expect(knife_ssh).to receive(:run) expect(knife.client_builder).not_to receive(:run) expect(knife.chef_vault_handler).not_to receive(:run).with(node_name: knife.config[:chef_node_name]) knife.run end end context "when the validation key is not present" do before do # this tests runs the old code path where we have a validation key, so we need to pass that check allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false) end it "creates the client (and possibly adds chef-vault items)" do expect(knife_ssh).to receive(:run) expect(knife.client_builder).to receive(:run) expect(knife.chef_vault_handler).to receive(:run).with(node_name: knife.config[:chef_node_name]) knife.run end end context "when the validation_key is nil" do before do # this tests runs the old code path where we have a validation key, so we need to pass that check for some plugins Chef::Config[:validation_key] = nil end it "creates the client and does not run client_builder or the chef_vault_handler" do expect(knife_ssh).to receive(:run) expect(knife.client_builder).not_to receive(:run) expect(knife.chef_vault_handler).not_to receive(:run) knife.run end end end describe "specifying ssl verification" do end end chef-12.3.0/spec/unit/knife/data_bag_edit_spec.rb0000644000004100000410000001064512520074675021643 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010 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 'tempfile' describe Chef::Knife::DataBagEdit do before do Chef::Config[:node_name] = "webmonkey.example.com" knife.name_args = [bag_name, item_name] allow(knife).to receive(:config).and_return(config) end let(:knife) do k = Chef::Knife::DataBagEdit.new allow(k).to receive(:rest).and_return(rest) allow(k.ui).to receive(:stdout).and_return(stdout) k end let(:raw_hash) { {"login_name" => "alphaomega", "id" => "item_name"} } let(:db) { Chef::DataBagItem.from_hash(raw_hash)} let(:raw_edited_hash) { {"login_name" => "rho", "id" => "item_name", "new_key" => "new_value"} } let(:rest) { double("Chef::REST") } let(:stdout) { StringIO.new } let(:bag_name) { "sudoing_admins" } let(:item_name) { "ME" } let(:secret) { "abc123SECRET" } let(:config) { {} } let(:is_encrypted?) { false } let(:transmitted_hash) { raw_edited_hash } let(:data_to_edit) { db } shared_examples_for "editing a data bag" do it "correctly edits then uploads the data bag" do expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(db) expect(knife).to receive(:encrypted?).with(db.raw_data).and_return(is_encrypted?) expect(knife).to receive(:edit_data).with(data_to_edit).and_return(raw_edited_hash) expect(rest).to receive(:put_rest).with("data/#{bag_name}/#{item_name}", transmitted_hash).ordered knife.run end end it "requires data bag and item arguments" do knife.name_args = [] expect(stdout).to receive(:puts).twice.with(anything) expect {knife.run}.to exit_with_code(1) expect(stdout.string).to eq("") end context "when no secret is provided" do include_examples "editing a data bag" end context "when config[:print_after] is set" do let(:config) { {:print_after => true} } before do expect(knife.ui).to receive(:output).with(raw_edited_hash) end include_examples "editing a data bag" end context "when a secret is provided" do let!(:enc_raw_hash) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_hash, secret) } let!(:enc_edited_hash) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_edited_hash, secret) } let(:transmitted_hash) { enc_edited_hash } before(:each) do expect(knife).to receive(:read_secret).at_least(1).times.and_return(secret) expect(Chef::EncryptedDataBagItem).to receive(:encrypt_data_bag_item).with(raw_edited_hash, secret).and_return(enc_edited_hash) end context "the data bag starts encrypted" do let(:is_encrypted?) { true } let(:db) { Chef::DataBagItem.from_hash(enc_raw_hash) } # If the data bag is encrypted, it gets passed to `edit` as a hash. Otherwise, it gets passed as a DataBag let (:data_to_edit) { raw_hash } before(:each) do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(true) end include_examples "editing a data bag" end context "the data bag starts unencrypted" do before(:each) do expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).exactly(0).times expect(knife).to receive(:encryption_secret_provided?).and_return(true) end include_examples "editing a data bag" end end it "fails to edit an encrypted data bag if the secret is missing" do expect(Chef::DataBagItem).to receive(:load).with(bag_name, item_name).and_return(db) expect(knife).to receive(:encrypted?).with(db.raw_data).and_return(true) expect(knife).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false) expect(knife.ui).to receive(:fatal).with("You cannot edit an encrypted data bag without providing the secret.") expect {knife.run}.to exit_with_code(1) end end chef-12.3.0/spec/unit/knife/environment_list_spec.rb0000644000004100000410000000350212520074675022505 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::EnvironmentList do before(:each) do @knife = Chef::Knife::EnvironmentList.new allow(@knife).to receive(:msg).and_return true allow(@knife).to receive(:output).and_return true allow(@knife).to receive(:show_usage).and_return true @environments = { "production" => "http://localhost:4000/environments/production", "development" => "http://localhost:4000/environments/development", "testing" => "http://localhost:4000/environments/testing" } allow(Chef::Environment).to receive(:list).and_return @environments end it "should make an api call to list the environments" do expect(Chef::Environment).to receive(:list) @knife.run end it "should print the environment names in a sorted list" do names = @environments.keys.sort { |a,b| a <=> b } expect(@knife).to receive(:output).with(names) @knife.run end describe "with --with-uri" do it "should print and unsorted list of the environments and their URIs" do @knife.config[:with_uri] = true expect(@knife).to receive(:output).with(@environments) @knife.run end end end chef-12.3.0/spec/unit/knife/node_run_list_remove_spec.rb0000644000004100000410000000617012520074675023333 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::NodeRunListRemove do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeRunListRemove.new @knife.config[:print_after] = nil @knife.name_args = [ "adam", "role[monkey]" ] @node = Chef::Node.new() @node.name("knifetest-node") @node.run_list << "role[monkey]" allow(@node).to receive(:save).and_return(true) allow(@knife.ui).to receive(:output).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Node).to receive(:load).and_return(@node) end describe "run" do it "should load the node" do expect(Chef::Node).to receive(:load).with("adam").and_return(@node) @knife.run end it "should remove the item from the run list" do @knife.run expect(@node.run_list[0]).not_to eq('role[monkey]') end it "should save the node" do expect(@node).to receive(:save).and_return(true) @knife.run end it "should print the run list" do @knife.config[:print_after] = true expect(@knife.ui).to receive(:output).with({ "knifetest-node" => { 'run_list' => [] } }) @knife.run end describe "run with a list of roles and recipes" do it "should remove the items from the run list" do @node.run_list << 'role[monkey]' @node.run_list << 'recipe[duck::type]' @knife.name_args = [ 'adam', 'role[monkey],recipe[duck::type]' ] @knife.run expect(@node.run_list).not_to include('role[monkey]') expect(@node.run_list).not_to include('recipe[duck::type]') end it "should remove the items from the run list when name args contains whitespace" do @node.run_list << 'role[monkey]' @node.run_list << 'recipe[duck::type]' @knife.name_args = [ 'adam', 'role[monkey], recipe[duck::type]' ] @knife.run expect(@node.run_list).not_to include('role[monkey]') expect(@node.run_list).not_to include('recipe[duck::type]') end it "should remove the items from the run list when name args contains multiple run lists" do @node.run_list << 'role[blah]' @node.run_list << 'recipe[duck::type]' @knife.name_args = [ 'adam', 'role[monkey], recipe[duck::type]', 'role[blah]' ] @knife.run expect(@node.run_list).not_to include('role[monkey]') expect(@node.run_list).not_to include('recipe[duck::type]') end end end end chef-12.3.0/spec/unit/knife/client_show_spec.rb0000644000004100000410000000344312520074675021430 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ClientShow do before(:each) do @knife = Chef::Knife::ClientShow.new @knife.name_args = [ 'adam' ] @client_mock = double('client_mock') end describe 'run' do it 'should list the client' do expect(Chef::ApiClient).to receive(:load).with('adam').and_return(@client_mock) expect(@knife).to receive(:format_for_display).with(@client_mock) @knife.run end it 'should pretty print json' do @knife.config[:format] = 'json' @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) fake_client_contents = {"foo"=>"bar", "baz"=>"qux"} expect(Chef::ApiClient).to receive(:load).with('adam').and_return(fake_client_contents) @knife.run expect(@stdout.string).to eql("{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}\n") end it 'should print usage and exit when a client name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/node_run_list_set_spec.rb0000644000004100000410000001116112520074675022625 0ustar www-datawww-data# # Author:: Mike Fiedler () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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::NodeRunListSet do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeRunListSet.new @knife.config = {} @knife.name_args = [ "adam", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @node = Chef::Node.new() allow(@node).to receive(:save).and_return(true) allow(Chef::Node).to receive(:load).and_return(@node) end describe "run" do it "should load the node" do expect(Chef::Node).to receive(:load).with("adam") @knife.run end it "should set the run list" do @knife.run expect(@node.run_list[0]).to eq('role[monkey]') end it "should save the node" do expect(@node).to receive(:save) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.run end describe "with more than one role or recipe" do it "should set the run list to all the entries" do @knife.name_args = [ "adam", "role[monkey],role[duck]" ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[duck]") end end describe "with more than one role or recipe with space between items" do it "should set the run list to all the entries" do @knife.name_args = [ "adam", "role[monkey], role[duck]" ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[duck]") end end describe "with more than one role or recipe as different arguments" do it "should set the run list to all the entries" do @knife.name_args = [ "adam", "role[monkey]", "role[duck]" ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[duck]") end end describe "with more than one role or recipe as different arguments and list separated by comas" do it "should add to the run list all the entries" do @knife.name_args = [ "adam", "role[monkey]", "role[duck],recipe[bird::fly]" ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[duck]") end end describe "with one role or recipe but with an extraneous comma" do it "should add to the run list one item" do @knife.name_args = [ "adam", "role[monkey]," ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") end end describe "with an existing run list" do it "should overwrite any existing run list items" do @node.run_list << "role[acorns]" @node.run_list << "role[zebras]" expect(@node.run_list[0]).to eq("role[acorns]") expect(@node.run_list[1]).to eq("role[zebras]") expect(@node.run_list.run_list_items.size).to eq(2) @knife.name_args = [ "adam", "role[monkey]", "role[duck]" ] @knife.run expect(@node.run_list[0]).to eq("role[monkey]") expect(@node.run_list[1]).to eq("role[duck]") expect(@node.run_list.run_list_items.size).to eq(2) end end describe "with no role or recipe" do # Set up outputs for inspection later before(:each) do @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) end it "should exit" do @knife.name_args = [ "adam" ] expect { @knife.run }.to raise_error SystemExit end it "should show the user" do @knife.name_args = [ "adam" ] begin ; @knife.run ; rescue SystemExit ; end expect(@stdout.string).to eq "USAGE: knife node run_list set NODE ENTRIES (options)\n" expect(@stderr.string).to eq "FATAL: You must supply both a node name and a run list.\n" end end end end chef-12.3.0/spec/unit/knife/configure_spec.rb0000644000004100000410000002604012520074675021071 0ustar www-datawww-datarequire 'spec_helper' describe Chef::Knife::Configure do before do Chef::Log.logger = Logger.new(StringIO.new) Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::Configure.new @rest_client = double("null rest client", :post_rest => { :result => :true }) allow(@knife).to receive(:rest).and_return(@rest_client) @out = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@out) @knife.config[:config_file] = '/home/you/.chef/knife.rb' @in = StringIO.new("\n" * 7) allow(@knife.ui).to receive(:stdin).and_return(@in) @err = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@err) allow(Ohai::System).to receive(:new).and_return(ohai) end let(:fqdn) { "foo.example.org" } let(:ohai) do o = {} allow(o).to receive(:require_plugin) allow(o).to receive(:load_plugins) o[:fqdn] = fqdn o end let(:default_admin_key) { "/etc/chef-server/admin.pem" } let(:default_admin_key_win32) { File.expand_path(default_admin_key) } let(:default_validator_key) { "/etc/chef-server/chef-validator.pem" } let(:default_validator_key_win32) { File.expand_path(default_validator_key) } let(:default_server_url) { "https://#{fqdn}:443" } it "asks the user for the URL of the chef server" do @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter the chef server URL: [#{default_server_url}]")) expect(@knife.chef_server).to eq(default_server_url) end it "asks the user for the clientname they want for the new client if -i is specified" do @knife.config[:initial] = true allow(Etc).to receive(:getlogin).and_return("a-new-user") @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter a name for the new user: [a-new-user]")) expect(@knife.new_client_name).to eq(Etc.getlogin) end it "should not ask the user for the clientname they want for the new client if -i and --node_name are specified" do @knife.config[:initial] = true @knife.config[:node_name] = 'testnode' allow(Etc).to receive(:getlogin).and_return("a-new-user") @knife.ask_user_for_config expect(@out.string).not_to match(Regexp.escape("Please enter a name for the new user")) expect(@knife.new_client_name).to eq('testnode') end it "asks the user for the existing API username or clientname if -i is not specified" do allow(Etc).to receive(:getlogin).and_return("a-new-user") @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter an existing username or clientname for the API: [a-new-user]")) expect(@knife.new_client_name).to eq(Etc.getlogin) end 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 expect(@out.string).to match(Regexp.escape("Please enter the existing admin name: [admin]")) expect(@knife.admin_client_name).to eq('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 expect(@out.string).not_to match(Regexp.escape("Please enter the existing admin:")) expect(@knife.admin_client_name).to eq('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 expect(@out.string).not_to match(Regexp.escape("Please enter the existing admin: [admin]")) expect(@knife.admin_client_name).not_to eq('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 expect(@out.string).to match(Regexp.escape("Please enter the location of the existing admin's private key: [#{default_admin_key}]")) if windows? expect(@knife.admin_client_key.capitalize).to eq(default_admin_key_win32.capitalize) else expect(@knife.admin_client_key).to eq(default_admin_key) end end it "should not ask the user for the location of the existing admin key if -i and --admin_client_key are specified" do @knife.config[:initial] = true @knife.config[:admin_client_key] = '/home/you/.chef/my-webui.pem' @knife.ask_user_for_config expect(@out.string).not_to match(Regexp.escape("Please enter the location of the existing admin client's private key:")) if windows? expect(@knife.admin_client_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-webui\.pem$} else expect(@knife.admin_client_key).to eq('/home/you/.chef/my-webui.pem') end end it "should not ask the user for the location of the existing admin key if -i is not specified" do @knife.ask_user_for_config expect(@out.string).not_to match(Regexp.escape("Please enter the location of the existing admin client's private key: [#{default_admin_key}]")) if windows? expect(@knife.admin_client_key).not_to eq(default_admin_key_win32) else expect(@knife.admin_client_key).not_to eq(default_admin_key) end end it "asks the user for the location of a chef repo" do @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter the path to a chef repository (or leave blank):")) expect(@knife.chef_repo).to eq('') end it "asks the users for the name of the validation client" do @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter the validation clientname: [chef-validator]")) expect(@knife.validation_client_name).to eq('chef-validator') end it "should not ask the users for the name of the validation client if --validation_client_name is specified" do @knife.config[:validation_client_name] = 'my-validator' @knife.ask_user_for_config expect(@out.string).not_to match(Regexp.escape("Please enter the validation clientname:")) expect(@knife.validation_client_name).to eq('my-validator') end it "asks the users for the location of the validation key" do @knife.ask_user_for_config expect(@out.string).to match(Regexp.escape("Please enter the location of the validation key: [#{default_validator_key}]")) if windows? expect(@knife.validation_key.capitalize).to eq(default_validator_key_win32.capitalize) else expect(@knife.validation_key).to eq(default_validator_key) end end it "should not ask the users for the location of the validation key if --validation_key is specified" do @knife.config[:validation_key] = '/home/you/.chef/my-validation.pem' @knife.ask_user_for_config expect(@out.string).not_to match(Regexp.escape("Please enter the location of the validation key:")) if windows? expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$} else expect(@knife.validation_key).to eq('/home/you/.chef/my-validation.pem') end end it "should not ask the user for anything if -i and all other properties are specified" do @knife.config[:initial] = true @knife.config[:chef_server_url] = 'http://localhost:5000' @knife.config[:node_name] = 'testnode' @knife.config[:admin_client_name] = 'my-webui' @knife.config[:admin_client_key] = '/home/you/.chef/my-webui.pem' @knife.config[:validation_client_name] = 'my-validator' @knife.config[:validation_key] = '/home/you/.chef/my-validation.pem' @knife.config[:repository] = '' @knife.config[:client_key] = '/home/you/a-new-user.pem' allow(Etc).to receive(:getlogin).and_return('a-new-user') @knife.ask_user_for_config expect(@out.string).to match(/\s*/) expect(@knife.new_client_name).to eq('testnode') expect(@knife.chef_server).to eq('http://localhost:5000') expect(@knife.admin_client_name).to eq('my-webui') if windows? expect(@knife.admin_client_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-webui\.pem$} expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$} expect(@knife.new_client_key).to match %r{^[A-Za-z]:/home/you/a-new-user\.pem$} else expect(@knife.admin_client_key).to eq('/home/you/.chef/my-webui.pem') expect(@knife.validation_key).to eq('/home/you/.chef/my-validation.pem') expect(@knife.new_client_key).to eq('/home/you/a-new-user.pem') end expect(@knife.validation_client_name).to eq('my-validator') expect(@knife.chef_repo).to eq('') end it "writes the new data to a config file" do allow(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb") allow(File).to receive(:expand_path).with("/home/you/.chef/#{Etc.getlogin}.pem").and_return("/home/you/.chef/#{Etc.getlogin}.pem") allow(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key) allow(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key) expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef") config_file = StringIO.new expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w").and_yield config_file @knife.config[:repository] = '/home/you/chef-repo' @knife.run expect(config_file.string).to match(/^node_name[\s]+'#{Etc.getlogin}'$/) expect(config_file.string).to match(%r{^client_key[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$}) expect(config_file.string).to match(/^validation_client_name\s+'chef-validator'$/) expect(config_file.string).to match(%r{^validation_key\s+'#{default_validator_key}'$}) expect(config_file.string).to match(%r{^chef_server_url\s+'#{default_server_url}'$}) expect(config_file.string).to match(%r{cookbook_path\s+\[ '/home/you/chef-repo/cookbooks' \]}) end it "creates a new client when given the --initial option" do expect(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb") expect(File).to receive(:expand_path).with("/home/you/.chef/a-new-user.pem").and_return("/home/you/.chef/a-new-user.pem") expect(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key) expect(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key) Chef::Config[:node_name] = "webmonkey.example.com" user_command = Chef::Knife::UserCreate.new expect(user_command).to receive(:run) allow(Etc).to receive(:getlogin).and_return("a-new-user") allow(Chef::Knife::UserCreate).to receive(:new).and_return(user_command) expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef") expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w") @knife.config[:initial] = true @knife.config[:user_password] = "blah" @knife.run expect(user_command.name_args).to eq(Array("a-new-user")) expect(user_command.config[:user_password]).to eq("blah") expect(user_command.config[:admin]).to be_truthy expect(user_command.config[:file]).to eq("/home/you/.chef/a-new-user.pem") expect(user_command.config[:yes]).to be_truthy expect(user_command.config[:disable_editing]).to be_truthy end end chef-12.3.0/spec/unit/knife/client_reregister_spec.rb0000644000004100000410000000405412520074675022622 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ClientReregister do before(:each) do @knife = Chef::Knife::ClientReregister.new @knife.name_args = [ 'adam' ] @client_mock = double('client_mock', :private_key => "foo_key") @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end context "when no client name is given on the command line" do before do @knife.name_args = [] end it 'should print usage and exit when a client name is not provided' do expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end context 'when not configured for file output' do it 'reregisters the client and prints the key' do expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock) @knife.run expect(@stdout.string).to match( /foo_key/ ) end end context 'when configured for file output' do it 'should write the private key to a file' do expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock) @knife.config[:file] = '/tmp/monkeypants' filehandle = StringIO.new expect(File).to receive(:open).with('/tmp/monkeypants', 'w').and_yield(filehandle) @knife.run expect(filehandle.string).to eq("foo_key") end end end chef-12.3.0/spec/unit/knife/environment_create_spec.rb0000644000004100000410000000525212520074675023001 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::EnvironmentCreate do before(:each) do @knife = Chef::Knife::EnvironmentCreate.new allow(@knife).to receive(:msg).and_return true allow(@knife).to receive(:output).and_return true allow(@knife).to receive(:show_usage).and_return true @knife.name_args = [ "production" ] @environment = Chef::Environment.new allow(@environment).to receive(:save) allow(Chef::Environment).to receive(:new).and_return @environment allow(@knife).to receive(:edit_data).and_return @environment end describe "run" do it "should create a new environment" do expect(Chef::Environment).to receive(:new) @knife.run end it "should set the environment name" do expect(@environment).to receive(:name).with("production") @knife.run end it "should not print the environment" do expect(@knife).not_to receive(:output) @knife.run end it "should prompt you to edit the data" do expect(@knife).to receive(:edit_data).with(@environment) @knife.run end it "should save the environment" do expect(@environment).to receive(:save) @knife.run end it "should show usage and exit when no environment name is provided" do @knife.name_args = [ ] expect(@knife.ui).to receive(:fatal) expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end describe "with --description" do before(:each) do @knife.config[:description] = "This is production" end it "should set the description" do expect(@environment).to receive(:description).with("This is production") @knife.run end end describe "with --print-after" do before(:each) do @knife.config[:print_after] = true end it "should pretty print the environment, formatted for display" do expect(@knife).to receive(:output).with(@environment) @knife.run end end end end chef-12.3.0/spec/unit/knife/cookbook_site_download_spec.rb0000644000004100000410000001356712520074675023643 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2012 Thomas Bishop # 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 File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') describe Chef::Knife::CookbookSiteDownload do describe 'run' do before do @knife = Chef::Knife::CookbookSiteDownload.new @knife.name_args = ['apache2'] @noauth_rest = double('no auth rest') @stderr = StringIO.new @cookbook_api_url = 'https://supermarket.chef.io/api/v1/cookbooks' @version = '1.0.2' @version_us = @version.gsub '.', '_' @current_data = { 'deprecated' => false, 'latest_version' => "#{@cookbook_api_url}/apache2/versions/#{@version_us}", 'replacement' => 'other_apache2' } allow(@knife.ui).to receive(:stderr).and_return(@stderr) allow(@knife).to receive(:noauth_rest).and_return(@noauth_rest) expect(@noauth_rest).to receive(:get_rest). with("#{@cookbook_api_url}/apache2"). and_return(@current_data) end context 'when the cookbook is deprecated and not forced' do before do @current_data['deprecated'] = true end it 'should warn with info about the replacement' do expect(@knife.ui).to receive(:warn). with(/.+deprecated.+replaced by other_apache2.+/i) expect(@knife.ui).to receive(:warn). with(/use --force.+download.+/i) @knife.run end end context 'when' do before do @cookbook_data = { 'version' => @version, 'file' => "http://example.com/apache2_#{@version_us}.tgz" } @temp_file = double( :path => "/tmp/apache2_#{@version_us}.tgz" ) @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz") expect(@noauth_rest).to receive(:sign_on_redirect=).with(false) end context 'downloading the latest version' do before do expect(@noauth_rest).to receive(:get_rest). with(@current_data['latest_version']). and_return(@cookbook_data) expect(@noauth_rest).to receive(:get_rest). with(@cookbook_data['file'], true). and_return(@temp_file) end context 'and it is deprecated and with --force' do before do @current_data['deprecated'] = true @knife.config[:force] = true end it 'should download the latest version' do expect(@knife.ui).to receive(:warn). with(/.+deprecated.+replaced by other_apache2.+/i) expect(FileUtils).to receive(:cp).with(@temp_file.path, @file) @knife.run expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i end end it 'should download the latest version' do expect(FileUtils).to receive(:cp).with(@temp_file.path, @file) @knife.run expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i end context 'with -f or --file' do before do @file = '/opt/chef/cookbooks/apache2.tar.gz' @knife.config[:file] = @file expect(FileUtils).to receive(:cp).with(@temp_file.path, @file) end it 'should download the cookbook to the desired file' do @knife.run expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i end end it 'should provide an accessor to the version' do allow(FileUtils).to receive(:cp).and_return(true) expect(@knife.version).to eq(@version) @knife.run end end context 'downloading a cookbook of a specific version' do before do @version = '1.0.1' @version_us = @version.gsub '.', '_' @cookbook_data = { 'version' => @version, 'file' => "http://example.com/apache2_#{@version_us}.tgz" } @temp_file = double(:path => "/tmp/apache2_#{@version_us}.tgz") @file = File.join(Dir.pwd, "apache2-#{@version}.tar.gz") @knife.name_args << @version end it 'should download the desired version' do expect(@noauth_rest).to receive(:get_rest). with("#{@cookbook_api_url}/apache2/versions/#{@version_us}"). and_return(@cookbook_data) expect(@noauth_rest).to receive(:get_rest). with(@cookbook_data['file'], true). and_return(@temp_file) expect(FileUtils).to receive(:cp).with(@temp_file.path, @file) @knife.run expect(@stderr.string).to match /downloading apache2.+version.+#{Regexp.escape(@version)}/i expect(@stderr.string).to match /cookbook save.+#{Regexp.escape(@file)}/i end end end end end chef-12.3.0/spec/unit/knife/cookbook_test_spec.rb0000644000004100000410000000614512520074675021761 0ustar www-datawww-data# # Author:: Stephen Delano ()$ # Author:: Matthew Kent () # Copyright:: Copyright (c) 2010 Opscode, Inc.$ # Copyright:: Copyright (c) 2010 Matthew Kent # 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::CookbookTest.load_deps describe Chef::Knife::CookbookTest do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::CookbookTest.new @knife.config[:cookbook_path] = File.join(CHEF_SPEC_DATA,'cookbooks') allow(@knife.cookbook_loader).to receive(:cookbook_exists?).and_return(true) @cookbooks = [] %w{tats central_market jimmy_johns pho}.each do |cookbook_name| @cookbooks << Chef::CookbookVersion.new(cookbook_name) end @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should test the cookbook" do allow(@knife).to receive(:test_cookbook).and_return(true) @knife.name_args = ["italian"] expect(@knife).to receive(:test_cookbook).with("italian") @knife.run end it "should test multiple cookbooks when provided" do allow(@knife).to receive(:test_cookbook).and_return(true) @knife.name_args = ["tats", "jimmy_johns"] expect(@knife).to receive(:test_cookbook).with("tats") expect(@knife).to receive(:test_cookbook).with("jimmy_johns") expect(@knife).not_to receive(:test_cookbook).with("central_market") expect(@knife).not_to receive(:test_cookbook).with("pho") @knife.run end it "should test both ruby and templates" do @knife.name_args = ["example"] expect(@knife.config[:cookbook_path]).not_to be_empty Array(@knife.config[:cookbook_path]).reverse.each do |path| expect(@knife).to receive(:test_ruby).with(an_instance_of(Chef::Cookbook::SyntaxCheck)) expect(@knife).to receive(:test_templates).with(an_instance_of(Chef::Cookbook::SyntaxCheck)) end @knife.run end describe "with -a or --all" do it "should test all of the cookbooks" do allow(@knife).to receive(:test_cookbook).and_return(true) @knife.config[:all] = true @loader = {} allow(@loader).to receive(:load_cookbooks).and_return(@loader) @cookbooks.each do |cookbook| @loader[cookbook.name] = cookbook end allow(@knife).to receive(:cookbook_loader).and_return(@loader) @loader.each do |key, cookbook| expect(@knife).to receive(:test_cookbook).with(cookbook.name) end @knife.run end end end end chef-12.3.0/spec/unit/knife/role_run_list_remove_spec.rb0000644000004100000410000000571612520074675023354 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleRunListRemove do before(:each) do Chef::Config[:role_name] = "will" @setup = Chef::Knife::RoleRunListAdd.new @setup.name_args = [ "will", "role[monkey]", "role[person]" ] @knife = Chef::Knife::RoleRunListRemove.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list[0]).to eq('role[person]') expect(@role.run_list[1]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "run with a list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will', 'role[monkey]' ] @knife.run @knife.name_args = [ 'will', 'recipe[duck::type]' ] @knife.run expect(@role.run_list).not_to include('role[monkey]') expect(@role.run_list).not_to include('recipe[duck::type]') expect(@role.run_list[0]).to eq('recipe[orange::chicken]') expect(@role.run_list[1]).to eq('role[person]') expect(@role.run_list[2]).to eq('role[bird]') expect(@role.run_list[3]).to eq('role[town]') end end end end chef-12.3.0/spec/unit/knife/cookbook_site_install_spec.rb0000644000004100000410000002041712520074675023472 0ustar www-datawww-data# # Author:: Steven Danna () # 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) describe Chef::Knife::CookbookSiteInstall do let(:knife) { Chef::Knife::CookbookSiteInstall.new } let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:downloader) { Hash.new } let(:repo) { double(:sanity_check => true, :reset_to_default_state => true, :prepare_to_import => true, :finalize_updates_to => true, :merge_updates_from => true) } let(:install_path) { if Chef::Platform.windows? 'C:/tmp/chef' else '/var/tmp/chef' end } before(:each) do require 'chef/knife/core/cookbook_scm_repo' allow(knife.ui).to receive(:stdout).and_return(stdout) knife.config = {} knife.config[:cookbook_path] = [ install_path ] allow(knife).to receive(:stderr).and_return(stderr) allow(knife).to receive(:stdout).and_return(stdout) # Assume all external commands would have succeed. :( allow(File).to receive(:unlink) allow(File).to receive(:rmtree) allow(knife).to receive(:shell_out!).and_return(true) # CookbookSiteDownload Stup allow(knife).to receive(:download_cookbook_to).and_return(downloader) allow(downloader).to receive(:version) do if knife.name_args.size == 2 knife.name_args[1] else "0.3.0" end end # Stubs for CookbookSCMRepo allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(repo) end describe "run" do it "raises an error if a cookbook name is not provided" do knife.name_args = [] expect(knife.ui).to receive(:error).with("Please specify a cookbook to download and install.") expect { knife.run }.to raise_error(SystemExit) end it "raises an error if more than two arguments are given" do knife.name_args = ["foo", "bar", "baz"] expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.") expect { knife.run }.to raise_error(SystemExit) end it "raises an error if the second argument is not a version" do knife.name_args = ["getting-started", "1pass"] expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.") expect { knife.run }.to raise_error(SystemExit) end it "raises an error if the second argument is a four-digit version" do knife.name_args = ["getting-started", "0.0.0.1"] expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.") expect { knife.run }.to raise_error(SystemExit) end it "raises an error if the second argument is a one-digit version" do knife.name_args = ["getting-started", "1"] expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.") expect { knife.run }.to raise_error(SystemExit) end it "installs the specified version if second argument is a three-digit version" do knife.name_args = ["getting-started", "0.1.0"] knife.config[:no_deps] = true upstream_file = File.join(install_path, "getting-started.tar.gz") expect(knife).to receive(:download_cookbook_to).with(upstream_file) expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0") expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started")) expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1.0") knife.run end it "installs the specified version if second argument is a two-digit version" do knife.name_args = ["getting-started", "0.1"] knife.config[:no_deps] = true upstream_file = File.join(install_path, "getting-started.tar.gz") expect(knife).to receive(:download_cookbook_to).with(upstream_file) expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1") expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started")) expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1") knife.run end it "installs the latest version if only a cookbook name is given" do knife.name_args = ["getting-started"] knife.config[:no_deps] = true upstream_file = File.join(install_path, "getting-started.tar.gz") expect(knife).to receive(:download_cookbook_to).with(upstream_file) expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0") expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started")) expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0") knife.run end it "does not create/reset git branches if use_current_branch is set" do knife.name_args = ["getting-started"] knife.config[:use_current_branch] = true knife.config[:no_deps] = true upstream_file = File.join(install_path, "getting-started.tar.gz") expect(repo).not_to receive(:prepare_to_import) expect(repo).not_to receive(:reset_to_default_state) knife.run end it "does not raise an error if cookbook_path is a string" do knife.config[:cookbook_path] = install_path knife.config[:no_deps] = true knife.name_args = ["getting-started"] upstream_file = File.join(install_path, "getting-started.tar.gz") expect(knife).to receive(:download_cookbook_to).with(upstream_file) expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0") expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started")) expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0") expect { knife.run }.not_to raise_error end end # end of run let(:metadata) { Chef::Cookbook::Metadata.new } let(:rb_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.rb") } let(:json_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.json") } describe "preferred_metadata" do before do allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata) allow(File).to receive(:exist?).and_return(false) knife.instance_variable_set(:@cookbook_name, "post-punk-kitchen") knife.instance_variable_set(:@install_path, install_path) end it "returns a populated Metadata object if metadata.rb exists" do allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true) expect(metadata).to receive(:from_file).with(rb_metadata_path) knife.preferred_metadata end it "returns a populated Metadata object if metadata.json exists" do allow(File).to receive(:exist?).with(json_metadata_path).and_return(true) #expect(IO).to receive(:read).with(json_metadata_path) allow(IO).to receive(:read) expect(metadata).to receive(:from_json) knife.preferred_metadata end it "prefers metadata.rb over metadata.json" do allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true) allow(File).to receive(:exist?).with(json_metadata_path).and_return(true) allow(IO).to receive(:read) expect(metadata).to receive(:from_file).with(rb_metadata_path) expect(metadata).not_to receive(:from_json) knife.preferred_metadata end it "rasies an error if it finds no metadata file" do expect { knife.preferred_metadata }.to raise_error { |error| expect(error).to be_a(Chef::Exceptions::MetadataNotFound) expect(error.cookbook_name).to eq("post-punk-kitchen") expect(error.install_path).to eq(install_path) } end end end chef-12.3.0/spec/unit/knife/raw_spec.rb0000644000004100000410000000262612520074675017705 0ustar www-datawww-data# # Author:: Steven Danna () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Chef::Knife::Raw do let(:rest) do r = double('Chef::Knife::Raw::RawInputServerAPI') allow(Chef::Knife::Raw::RawInputServerAPI).to receive(:new).and_return(r) r end let(:knife) do k = Chef::Knife::Raw.new k.config[:method] = "GET" k.name_args = [ "/nodes" ] k end describe "run" do it "should set the x-ops-request-source header when --proxy-auth is set" do knife.config[:proxy_auth] = true expect(rest).to receive(:request).with(:GET, "/nodes", { 'Content-Type' => 'application/json', 'x-ops-request-source' => 'web'}, false) knife.run end end end chef-12.3.0/spec/unit/knife/environment_edit_spec.rb0000644000004100000410000000501012520074675022453 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::EnvironmentEdit do before(:each) do @knife = Chef::Knife::EnvironmentEdit.new allow(@knife.ui).to receive(:msg).and_return true allow(@knife.ui).to receive(:output).and_return true allow(@knife.ui).to receive(:show_usage).and_return true @knife.name_args = [ "production" ] @environment = Chef::Environment.new @environment.name("production") @environment.description("Please edit me") allow(@environment).to receive(:save).and_return true allow(Chef::Environment).to receive(:load).and_return @environment allow(@knife.ui).to receive(:edit_data).and_return @environment end it "should load the environment" do expect(Chef::Environment).to receive(:load).with("production") @knife.run end it "should let you edit the environment" do expect(@knife.ui).to receive(:edit_data).with(@environment) @knife.run end it "should save the edited environment data" do pansy = Chef::Environment.new @environment.name("new_environment_name") expect(@knife.ui).to receive(:edit_data).with(@environment).and_return(pansy) expect(pansy).to receive(:save) @knife.run end it "should not save the unedited environment data" do expect(@environment).not_to receive(:save) @knife.run end it "should not print the environment" do expect(@knife).not_to receive(:output) @knife.run end it "shoud show usage and exit when no environment name is provided" do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect { @knife.run }.to raise_error(SystemExit) end describe "with --print-after" do it "should pretty print the environment, formatted for display" do @knife.config[:print_after] = true expect(@knife.ui).to receive(:output).with(@environment) @knife.run end end end chef-12.3.0/spec/unit/knife/cookbook_bulk_delete_spec.rb0000644000004100000410000000661012520074675023256 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::CookbookBulkDelete do before(:each) do Chef::Log.logger = Logger.new(StringIO.new) Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::CookbookBulkDelete.new @knife.config = {:print_after => nil} @knife.name_args = ["."] @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) allow(@knife.ui).to receive(:confirm).and_return(true) @cookbooks = Hash.new %w{cheezburger pizza lasagna}.each do |cookbook_name| cookbook = Chef::CookbookVersion.new(cookbook_name) @cookbooks[cookbook_name] = cookbook end @rest = double("Chef::REST") allow(@rest).to receive(:get_rest).and_return(@cookbooks) allow(@rest).to receive(:delete_rest).and_return(true) allow(@knife).to receive(:rest).and_return(@rest) allow(Chef::CookbookVersion).to receive(:list).and_return(@cookbooks) end describe "when there are several cookbooks on the server" do before do @cheezburger = {'cheezburger' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-cheez", "version" => "1.0.0"}]}} allow(@rest).to receive(:get_rest).with('cookbooks/cheezburger').and_return(@cheezburger) @pizza = {'pizza' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-pizza", "version" => "2.0.0"}]}} allow(@rest).to receive(:get_rest).with('cookbooks/pizza').and_return(@pizza) @lasagna = {'lasagna' => {"url" => "file:///dev/null", "versions" => [{"url" => "file:///dev/null-lasagna", "version" => "3.0.0"}]}} allow(@rest).to receive(:get_rest).with('cookbooks/lasagna').and_return(@lasagna) end it "should print the cookbooks you are about to delete" do expected = @knife.ui.list(@cookbooks.keys.sort, :columns_down) @knife.run expect(@stdout.string).to match(/#{expected}/) end it "should confirm you really want to delete them" do expect(@knife.ui).to receive(:confirm) @knife.run end it "should delete each cookbook" do {"cheezburger" => "1.0.0", "pizza" => "2.0.0", "lasagna" => '3.0.0'}.each do |cookbook_name, version| expect(@rest).to receive(:delete_rest).with("cookbooks/#{cookbook_name}/#{version}") end @knife.run end it "should only delete cookbooks that match the regex" do @knife.name_args = ["cheezburger"] expect(@rest).to receive(:delete_rest).with('cookbooks/cheezburger/1.0.0') @knife.run end end it "should exit if the regex is not provided" do @knife.name_args = [] expect { @knife.run }.to raise_error(SystemExit) end end chef-12.3.0/spec/unit/knife/environment_compare_spec.rb0000644000004100000410000000703012520074675023160 0ustar www-datawww-data# # Author:: Sander Botman () # Copyright:: Copyright (c) 2013 Sander Botman. # 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::EnvironmentCompare do before(:each) do @knife = Chef::Knife::EnvironmentCompare.new @environments = { "cita" => "http://localhost:4000/environments/cita", "citm" => "http://localhost:4000/environments/citm" } allow(@knife).to receive(:environment_list).and_return(@environments) @constraints = { "cita" => { "foo" => "= 1.0.1", "bar" => "= 0.0.4" }, "citm" => { "foo" => "= 1.0.1", "bar" => "= 0.0.2" } } allow(@knife).to receive(:constraint_list).and_return(@constraints) @cookbooks = { "foo"=>"= 1.0.1", "bar"=>"= 0.0.1" } allow(@knife).to receive(:cookbook_list).and_return(@cookbooks) @rest_double = double('rest') allow(@knife).to receive(:rest).and_return(@rest_double) @cookbook_names = ['apache2', 'mysql', 'foo', 'bar', 'dummy', 'chef_handler'] @base_url = 'https://server.example.com/cookbooks' @cookbook_data = {} @cookbook_names.each do |item| @cookbook_data[item] = {'url' => "#{@base_url}/#{item}", 'versions' => [{'version' => '1.0.1', 'url' => "#{@base_url}/#{item}/1.0.1"}]} end allow(@rest_double).to receive(:get_rest).with("/cookbooks?num_versions=1").and_return(@cookbook_data) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe 'run' do it 'should display only cookbooks with version constraints' do @knife.config[:format] = 'summary' @knife.run @environments.each do |item, url| expect(@stdout.string).to match /#{item}/ and expect(@stdout.string.lines.count).to be 4 end end it 'should display 4 number of lines' do @knife.config[:format] = 'summary' @knife.run expect(@stdout.string.lines.count).to be 4 end end describe 'with -m or --mismatch' do it 'should display only cookbooks that have mismatching version constraints' do @knife.config[:format] = 'summary' @knife.config[:mismatch] = true @knife.run @constraints.each do |item, ver| expect(@stdout.string).to match /#{ver[1]}/ end end it 'should display 3 number of lines' do @knife.config[:format] = 'summary' @knife.config[:mismatch] = true @knife.run expect(@stdout.string.lines.count).to be 3 end end describe 'with -a or --all' do it 'should display all cookbooks' do @knife.config[:format] = 'summary' @knife.config[:all] = true @knife.run @constraints.each do |item, ver| expect(@stdout.string).to match /#{ver[1]}/ end end it 'should display 8 number of lines' do @knife.config[:format] = 'summary' @knife.config[:all] = true @knife.run expect(@stdout.string.lines.count).to be 8 end end end chef-12.3.0/spec/unit/knife/role_env_run_list_set_spec.rb0000644000004100000410000000707512520074675023522 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleEnvRunListSet do before(:each) do Chef::Config[:role_name] = "will" Chef::Config[:env_name] = "QA" @setup = Chef::Knife::RoleEnvRunListAdd.new @setup.name_args = [ "will", "QA", "role[monkey]", "role[person]", "role[bucket]" ] @knife = Chef::Knife::RoleEnvRunListSet.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "QA", "role[owen]", "role[mauntel]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should replace all the items in the runlist with what is specified" do @setup.run @knife.run expect(@role.run_list_for('QA')[0]).to eq("role[owen]") expect(@role.run_list_for('QA')[1]).to eq("role[mauntel]") expect(@role.run_list_for('QA')[2]).to be_nil expect(@role.run_list[0]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "should clear an environmental run list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "QA", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @setup.name_args = [ "will", "PRD", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ "will", "QA", "role[coke]", "role[pepsi]" ] @knife.run expect(@role.run_list_for('QA')[0]).to eq("role[coke]") expect(@role.run_list_for('QA')[1]).to eq("role[pepsi]") expect(@role.run_list_for('QA')[2]).to be_nil expect(@role.run_list_for('PRD')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('PRD')[1]).to eq('role[monkey]') expect(@role.run_list_for('PRD')[2]).to eq('recipe[duck::type]') expect(@role.run_list_for('PRD')[3]).to eq('role[person]') expect(@role.run_list_for('PRD')[4]).to eq('role[bird]') expect(@role.run_list_for('PRD')[5]).to eq('role[town]') expect(@role.run_list[0]).to be_nil end end end end chef-12.3.0/spec/unit/knife/knife_help.rb0000644000004100000410000000605112520074675020202 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'spec_helper' describe Chef::Knife::Help do before(:each) do # Perilously use the build in list even though it is dynamic so we don't get warnings about the constant # HELP_TOPICS = [ "foo", "bar", "knife-kittens", "ceiling-cat", "shell" ] @knife = Chef::Knife::Help.new end it "should return a list of help topics" do expect(@knife.help_topics).to include("knife-status") end it "should run man for you" do @knife.name_args = [ "shell" ] expect(@knife).to receive(:exec).with(/^man \/.*\/shell.1$/) @knife.run end it "should suggest topics" do @knife.name_args = [ "list" ] allow(@knife.ui).to receive(:msg) expect(@knife.ui).to receive(:info).with("Available help topics are: ") expect(@knife.ui).to receive(:msg).with(/knife/) allow(@knife).to receive(:exec) expect(@knife).to receive(:exit).with(1) @knife.run end describe "find_manpage_path" do it "should find the man page in the gem" do expect(@knife.find_manpage_path("shell")).to match(/distro\/common\/man\/man1\/chef-shell.1$/) end it "should provide the man page name if not in the gem" do expect(@knife.find_manpage_path("foo")).to eq("foo") end end describe "find_manpages_for_query" do it "should error if it does not find a match" do allow(@knife.ui).to receive(:error) allow(@knife.ui).to receive(:info) allow(@knife.ui).to receive(:msg) expect(@knife).to receive(:exit).with(1) expect(@knife.ui).to receive(:error).with("No help found for 'chickens'") expect(@knife.ui).to receive(:msg).with(/knife/) @knife.find_manpages_for_query("chickens") end end describe "print_help_topics" do it "should print the known help topics" do allow(@knife.ui).to receive(:msg) allow(@knife.ui).to receive(:info) expect(@knife.ui).to receive(:msg).with(/knife/) @knife.print_help_topics end it "should shorten topics prefixed by knife-" do allow(@knife.ui).to receive(:msg) allow(@knife.ui).to receive(:info) expect(@knife.ui).to receive(:msg).with(/node/) @knife.print_help_topics end it "should not leave topics prefixed by knife-" do allow(@knife.ui).to receive(:msg) allow(@knife.ui).to receive(:info) expect(@knife.ui).not_to receive(:msg).with(/knife-node/) @knife.print_help_topics end end end chef-12.3.0/spec/unit/knife/user_create_spec.rb0000644000004100000410000000556012520074675021415 0ustar www-datawww-data# # 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' Chef::Knife::UserCreate.load_deps describe Chef::Knife::UserCreate do before(:each) do @knife = Chef::Knife::UserCreate.new @stdout = StringIO.new @stderr = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) allow(@knife.ui).to receive(:stderr).and_return(@stderr) @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' allow(@user).to receive(:create).and_return(@user_with_private_key) allow(Chef::User).to receive(:new).and_return(@user) allow(Chef::User).to receive(:from_hash).and_return(@user) allow(@knife).to receive(:edit_data).and_return(@user.to_hash) end it "creates a new user" do expect(Chef::User).to receive(:new).and_return(@user) expect(@user).to receive(:create) @knife.run expect(@stderr.string).to match /created user.+a_user/i end it "sets the password" do @knife.config[:user_password] = "a_password" expect(@user).to receive(:password).with("a_password") @knife.run end it "exits with an error if password is blank" do @knife.config[:user_password] = '' expect { @knife.run }.to raise_error SystemExit expect(@stderr.string).to match /You must specify a non-blank password/ end it "sets the user name" do expect(@user).to receive(:name).with("a_user") @knife.run end it "sets the public key if given" do @knife.config[:user_key] = "/a/filename" allow(File).to receive(:read).with(File.expand_path("/a/filename")).and_return("a_key") expect(@user).to receive(:public_key).with("a_key") @knife.run end it "allows you to edit the data" do expect(@knife).to 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 = double("filehandle") expect(filehandle).to receive(:print).with('private_key') expect(File).to receive(:open).with("/tmp/a_file", "w").and_yield(filehandle) @knife.run end end chef-12.3.0/spec/unit/knife/node_edit_spec.rb0000644000004100000410000000710012520074675021036 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' Chef::Knife::NodeEdit.load_deps describe Chef::Knife::NodeEdit do # helper to convert the view from Chef objects into Ruby objects representing JSON def deserialized_json_view actual = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json_pretty(@knife.node_editor.send(:view))) end before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::NodeEdit.new @knife.config = { :editor => 'cat', :attribute => nil, :print_after => nil } @knife.name_args = [ "adam" ] @node = Chef::Node.new() end it "should load the node" do expect(Chef::Node).to receive(:load).with("adam").and_return(@node) @knife.node end describe "after loading the node" do before do allow(@knife).to receive(:node).and_return(@node) @node.automatic_attrs = {:go => :away} @node.default_attrs = {:hide => :me} @node.override_attrs = {:dont => :show} @node.normal_attrs = {:do_show => :these} @node.chef_environment("prod") @node.run_list("recipe[foo]") end it "creates a view of the node without attributes from roles or ohai" do actual = deserialized_json_view expect(actual).not_to have_key("automatic") expect(actual).not_to have_key("override") expect(actual).not_to have_key("default") expect(actual["normal"]).to eq({"do_show" => "these"}) expect(actual["run_list"]).to eq(["recipe[foo]"]) expect(actual["chef_environment"]).to eq("prod") end it "shows the extra attributes when given the --all option" do @knife.config[:all_attributes] = true actual = deserialized_json_view expect(actual["automatic"]).to eq({"go" => "away"}) expect(actual["override"]).to eq({"dont" => "show"}) expect(actual["default"]).to eq({"hide" => "me"}) expect(actual["normal"]).to eq({"do_show" => "these"}) expect(actual["run_list"]).to eq(["recipe[foo]"]) expect(actual["chef_environment"]).to eq("prod") end it "does not consider unedited data updated" do view = deserialized_json_view @knife.node_editor.send(:apply_updates, view) expect(@knife.node_editor).not_to be_updated end it "considers edited data updated" do view = deserialized_json_view view["run_list"] << "role[fuuu]" @knife.node_editor.send(:apply_updates, view) expect(@knife.node_editor).to be_updated end end describe "edit_node" do before do allow(@knife).to receive(:node).and_return(@node) end let(:subject) { @knife.node_editor.edit_node } it "raises an exception when editing is disabled" do @knife.config[:disable_editing] = true expect{ subject }.to raise_error(SystemExit) end it "raises an exception when the editor is not set" do @knife.config[:editor] = nil expect{ subject }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/role_create_spec.rb0000644000004100000410000000446612520074675021404 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::Knife::RoleCreate do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::RoleCreate.new @knife.config = { :description => nil } @knife.name_args = [ "adam" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() allow(@role).to receive(:save) allow(Chef::Role).to receive(:new).and_return(@role) allow(@knife).to receive(:edit_data).and_return(@role) @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do it "should create a new role" do expect(Chef::Role).to receive(:new).and_return(@role) @knife.run end it "should set the role name" do expect(@role).to receive(:name).with("adam") @knife.run end it "should not print the role" do expect(@knife).not_to receive(:output) @knife.run end it "should allow you to edit the data" do expect(@knife).to receive(:edit_data).with(@role) @knife.run end it "should save the role" do expect(@role).to receive(:save) @knife.run end describe "with -d or --description" do it "should set the description" do @knife.config[:description] = "All is bob" expect(@role).to receive(:description).with("All is bob") @knife.run end end describe "with -p or --print-after" do it "should pretty print the node, formatted for display" do @knife.config[:print_after] = true expect(@knife).to receive(:output).with(@role) @knife.run end end end end chef-12.3.0/spec/unit/knife/role_env_run_list_remove_spec.rb0000644000004100000410000000737012520074675024222 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleEnvRunListRemove do before(:each) do Chef::Config[:role_name] = "will" Chef::Config[:env_name] = "QA" @setup = Chef::Knife::RoleEnvRunListAdd.new @setup.name_args = [ "will", "QA", "role[monkey]", "role[person]" ] @knife = Chef::Knife::RoleEnvRunListRemove.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "QA", "role[monkey]" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list_for('QA')[0]).not_to eq('role[monkey]') expect(@role.run_list_for('QA')[0]).to eq('role[person]') expect(@role.run_list[0]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "run with a list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "QA", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @setup.name_args = [ "will", "PRD", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will', 'QA', 'role[monkey]' ] @knife.run @knife.name_args = [ 'will', 'QA', 'recipe[duck::type]' ] @knife.run expect(@role.run_list_for('QA')).not_to include('role[monkey]') expect(@role.run_list_for('QA')).not_to include('recipe[duck::type]') expect(@role.run_list_for('QA')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('QA')[1]).to eq('role[person]') expect(@role.run_list_for('QA')[2]).to eq('role[bird]') expect(@role.run_list_for('QA')[3]).to eq('role[town]') expect(@role.run_list_for('PRD')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('PRD')[1]).to eq('role[monkey]') expect(@role.run_list_for('PRD')[2]).to eq('recipe[duck::type]') expect(@role.run_list_for('PRD')[3]).to eq('role[person]') expect(@role.run_list_for('PRD')[4]).to eq('role[bird]') expect(@role.run_list_for('PRD')[5]).to eq('role[town]') end end end end chef-12.3.0/spec/unit/knife/tag_list_spec.rb0000644000004100000410000000115612520074675020717 0ustar www-datawww-datarequire 'spec_helper' describe Chef::Knife::TagList do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" @knife = Chef::Knife::TagList.new @knife.name_args = [ Chef::Config[:node_name], "sadtag" ] @node = Chef::Node.new allow(@node).to receive :save @node.tags << "sadtag" << "happytag" allow(Chef::Node).to receive(:load).and_return @node end describe "run" do it "can list tags on a node" do expected = %w(sadtag happytag) expect(@node.tags).to eq(expected) expect(@knife).to receive(:output).with(expected) @knife.run end end end chef-12.3.0/spec/unit/knife/user_delete_spec.rb0000644000004100000410000000233612520074675021412 0ustar www-datawww-data# # 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::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 expect(@knife).to 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 = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end chef-12.3.0/spec/unit/knife/role_run_list_clear_spec.rb0000644000004100000410000000477112520074675023145 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleRunListClear do before(:each) do Chef::Config[:role_name] = "will" @setup = Chef::Knife::RoleRunListAdd.new @setup.name_args = [ "will", "role[monkey]", "role[person]" ] @knife = Chef::Knife::RoleRunListClear.new @knife.config = { :print_after => nil } @knife.name_args = [ "will" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list[0]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "should clear an environmental run list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will' ] @knife.run expect(@role.run_list[0]).to be_nil end end end end chef-12.3.0/spec/unit/knife/ssl_check_spec.rb0000644000004100000410000001767112520074675021060 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require "spec_helper" require 'stringio' describe Chef::Knife::SslCheck do let(:name_args) { [] } let(:stdout_io) { StringIO.new } let(:stderr_io) { StringIO.new } def stderr stderr_io.string end def stdout stdout_io.string end subject(:ssl_check) do s = Chef::Knife::SslCheck.new allow(s.ui).to receive(:stdout).and_return(stdout_io) allow(s.ui).to receive(:stderr).and_return(stderr_io) s.name_args = name_args s end before do Chef::Config.chef_server_url = "https://example.com:8443/chef-server" end context "when no arguments are given" do it "uses the chef_server_url as the host to check" do expect(ssl_check.host).to eq("example.com") expect(ssl_check.port).to eq(8443) end end context "when a specific URI is given" do let(:name_args) { %w{https://example.test:10443/foo} } it "checks the SSL configuration against the given host" do expect(ssl_check.host).to eq("example.test") expect(ssl_check.port).to eq(10443) end end context "when an invalid URI is given" do let(:name_args) { %w{foo.test} } it "prints an error and exits" do expect { ssl_check.run }.to raise_error(SystemExit) expected_stdout=<<-E USAGE: knife ssl check [URL] (options) E expected_stderr=<<-E ERROR: Given URI: `foo.test' is invalid E expect(stdout_io.string).to eq(expected_stdout) expect(stderr_io.string).to eq(expected_stderr) end context "and its malformed enough to make URI.parse barf" do let(:name_args) { %w{ftp://lkj\\blah:example.com/blah} } it "prints an error and exits" do expect { ssl_check.run }.to raise_error(SystemExit) expected_stdout=<<-E USAGE: knife ssl check [URL] (options) E expected_stderr=<<-E ERROR: Given URI: `#{name_args[0]}' is invalid E expect(stdout_io.string).to eq(expected_stdout) expect(stderr_io.string).to eq(expected_stderr) end end end describe "verifying trusted certificate X509 properties" do let(:name_args) { %w{https://foo.example.com:8443} } let(:trusted_certs_dir) { File.join(CHEF_SPEC_DATA, "trusted_certs") } let(:trusted_cert_file) { File.join(trusted_certs_dir, "example.crt") } let(:store) { OpenSSL::X509::Store.new } let(:certificate) { OpenSSL::X509::Certificate.new(IO.read(trusted_cert_file)) } before do Chef::Config[:trusted_certs_dir] = trusted_certs_dir allow(ssl_check).to receive(:trusted_certificates).and_return([trusted_cert_file]) allow(store).to receive(:add_cert).with(certificate) allow(OpenSSL::X509::Store).to receive(:new).and_return(store) allow(OpenSSL::X509::Certificate).to receive(:new).with(IO.read(trusted_cert_file)).and_return(certificate) allow(ssl_check).to receive(:verify_cert).and_return(true) allow(ssl_check).to receive(:verify_cert_host).and_return(true) end context "when the trusted certificates have valid X509 properties" do before do allow(store).to receive(:verify).with(certificate).and_return(true) end it "does not generate any X509 warnings" do expect(ssl_check.ui).not_to receive(:warn).with(/There are invalid certificates in your trusted_certs_dir/) ssl_check.run end end context "when the trusted certificates have invalid X509 properties" do before do allow(store).to receive(:verify).with(certificate).and_return(false) allow(store).to receive(:error_string).and_return("unable to get local issuer certificate") end it "generates a warning message with invalid certificate file names" do expect(ssl_check.ui).to receive(:warn).with(/#{trusted_cert_file}: unable to get local issuer certificate/) ssl_check.run end end end describe "verifying the remote certificate" do let(:name_args) { %w{https://foo.example.com:8443} } let(:tcp_socket) { double(TCPSocket) } let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket) } before do expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket) expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_check.verify_peer_ssl_context).and_return(ssl_socket) end def run ssl_check.run rescue Exception #puts "OUT: #{stdout_io.string}" #puts "ERR: #{stderr_io.string}" raise end context "when the remote host's certificate is valid" do before do expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs (no warn) expect(ssl_socket).to receive(:connect) # no error expect(ssl_socket).to receive(:post_connection_check).with("foo.example.com") # no error end it "prints a success message" do ssl_check.run expect(stdout_io.string).to include("Successfully verified certificates from `foo.example.com'") end end describe "and the certificate is not valid" do let(:tcp_socket_for_debug) { double(TCPSocket) } let(:ssl_socket_for_debug) { double(OpenSSL::SSL::SSLSocket) } let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt") } let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) } before do trap(:INT, "DEFAULT") expect(TCPSocket).to receive(:new). with("foo.example.com", 8443). and_return(tcp_socket_for_debug) expect(OpenSSL::SSL::SSLSocket).to receive(:new). with(tcp_socket_for_debug, ssl_check.noverify_peer_ssl_context). and_return(ssl_socket_for_debug) end context "when the certificate's CN does not match the hostname" do before do expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs expect(ssl_socket).to receive(:connect) # no error expect(ssl_socket).to receive(:post_connection_check). with("foo.example.com"). and_raise(OpenSSL::SSL::SSLError) expect(ssl_socket_for_debug).to receive(:connect) expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt) end it "shows the CN used by the certificate and prints an error" do expect { run }.to raise_error(SystemExit) expect(stderr).to include("The SSL cert is signed by a trusted authority but is not valid for the given hostname") expect(stderr).to include("You are attempting to connect to: 'foo.example.com'") expect(stderr).to include("The server's certificate belongs to 'example.local'") end end context "when the cert is not signed by any trusted authority" do before do expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs expect(ssl_socket).to receive(:connect). and_raise(OpenSSL::SSL::SSLError) expect(ssl_socket_for_debug).to receive(:connect) expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt) end it "shows the CN used by the certificate and prints an error" do expect { run }.to raise_error(SystemExit) expect(stderr).to include("The SSL certificate of foo.example.com could not be verified") end end end end end chef-12.3.0/spec/unit/knife/role_env_run_list_clear_spec.rb0000644000004100000410000000632112520074675024006 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Will Albenzi () # 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' describe Chef::Knife::RoleEnvRunListClear do before(:each) do Chef::Config[:role_name] = "will" Chef::Config[:env_name] = "QA" @setup = Chef::Knife::RoleEnvRunListAdd.new @setup.name_args = [ "will", "QA", "role[monkey]", "role[person]" ] @knife = Chef::Knife::RoleEnvRunListClear.new @knife.config = { :print_after => nil } @knife.name_args = [ "will", "QA" ] allow(@knife).to receive(:output).and_return(true) @role = Chef::Role.new() @role.name("will") allow(@role).to receive(:save).and_return(true) allow(@knife.ui).to receive(:confirm).and_return(true) allow(Chef::Role).to receive(:load).and_return(@role) end describe "run" do # it "should display all the things" do # @knife.run # @role.to_json.should == 'show all the things' # end it "should load the node" do expect(Chef::Role).to receive(:load).with("will").and_return(@role) @knife.run end it "should remove the item from the run list" do @setup.run @knife.run expect(@role.run_list_for('QA')[0]).to be_nil expect(@role.run_list[0]).to be_nil end it "should save the node" do expect(@role).to receive(:save).and_return(true) @knife.run end it "should print the run list" do expect(@knife).to receive(:output).and_return(true) @knife.config[:print_after] = true @setup.run @knife.run end describe "should clear an environmental run list of roles and recipes" do it "should remove the items from the run list" do @setup.name_args = [ "will", "QA", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @setup.name_args = [ "will", "PRD", "recipe[orange::chicken]", "role[monkey]", "recipe[duck::type]", "role[person]", "role[bird]", "role[town]" ] @setup.run @knife.name_args = [ 'will', 'QA' ] @knife.run expect(@role.run_list_for('QA')[0]).to be_nil expect(@role.run_list_for('PRD')[0]).to eq('recipe[orange::chicken]') expect(@role.run_list_for('PRD')[1]).to eq('role[monkey]') expect(@role.run_list_for('PRD')[2]).to eq('recipe[duck::type]') expect(@role.run_list_for('PRD')[3]).to eq('role[person]') expect(@role.run_list_for('PRD')[4]).to eq('role[bird]') expect(@role.run_list_for('PRD')[5]).to eq('role[town]') end end end end chef-12.3.0/spec/unit/knife/cookbook_list_spec.rb0000644000004100000410000000615412520074675021755 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::CookbookList do before do @knife = Chef::Knife::CookbookList.new @rest_mock = double('rest') allow(@knife).to receive(:rest).and_return(@rest_mock) @cookbook_names = ['apache2', 'mysql'] @base_url = 'https://server.example.com/cookbooks' @cookbook_data = {} @cookbook_names.each do |item| @cookbook_data[item] = {'url' => "#{@base_url}/#{item}", 'versions' => [{'version' => '1.0.1', 'url' => "#{@base_url}/#{item}/1.0.1"}]} end @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe 'run' do it 'should display the latest version of the cookbooks' do expect(@rest_mock).to receive(:get_rest).with('/cookbooks?num_versions=1'). and_return(@cookbook_data) @knife.run @cookbook_names.each do |item| expect(@stdout.string).to match /#{item}\s+1\.0\.1/ end end it 'should query cookbooks for the configured environment' do @knife.config[:environment] = 'production' expect(@rest_mock).to receive(:get_rest). with('/environments/production/cookbooks?num_versions=1'). and_return(@cookbook_data) @knife.run end describe 'with -w or --with-uri' do it 'should display the cookbook uris' do @knife.config[:with_uri] = true allow(@rest_mock).to receive(:get_rest).and_return(@cookbook_data) @knife.run @cookbook_names.each do |item| pattern = /#{Regexp.escape(@cookbook_data[item]['versions'].first['url'])}/ expect(@stdout.string).to match pattern end end end describe 'with -a or --all' do before do @cookbook_names.each do |item| @cookbook_data[item]['versions'] << {'version' => '1.0.0', 'url' => "#{@base_url}/#{item}/1.0.0"} end end it 'should display all versions of the cookbooks' do @knife.config[:all_versions] = true expect(@rest_mock).to receive(:get_rest).with('/cookbooks?num_versions=all'). and_return(@cookbook_data) @knife.run @cookbook_names.each do |item| expect(@stdout.string).to match /#{item}\s+1\.0\.1\s+1\.0\.0/ end end end end end chef-12.3.0/spec/unit/knife/user_list_spec.rb0000644000004100000410000000170712520074675021124 0ustar www-datawww-data# # 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 expect(Chef::User).to receive(:list) expect(@knife).to receive(:format_list_for_display) @knife.run end end chef-12.3.0/spec/unit/knife/status_spec.rb0000644000004100000410000000763512520074675020444 0ustar www-datawww-data# # Author:: Sahil Muthoo () # 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::Status do before(:each) do node = Chef::Node.new.tap do |n| n.automatic_attrs["fqdn"] = "foobar" n.automatic_attrs["ohai_time"] = 1343845969 end allow(Time).to receive(:now).and_return(Time.at(1428573420)) @query = double("Chef::Search::Query") allow(@query).to receive(:search).and_yield(node) allow(Chef::Search::Query).to receive(:new).and_return(@query) @knife = Chef::Knife::Status.new @stdout = StringIO.new allow(@knife.ui).to receive(:stdout).and_return(@stdout) end describe "run" do let(:opts) {{filter_result: { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"], ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"], platform_version: ["platform_version"], chef_environment: ["chef_environment"]}}} it "should default to searching for everything" do expect(@query).to receive(:search).with(:node, "*:*", opts) @knife.run end it "should filter healthy nodes" do @knife.config[:hide_healthy] = true expect(@query).to receive(:search).with(:node, "NOT ohai_time:[1428569820 TO 1428573420]", opts) @knife.run end it "should filter by environment" do @knife.config[:environment] = "production" expect(@query).to receive(:search).with(:node, "chef_environment:production", opts) @knife.run end it "should filter by environment and health" do @knife.config[:environment] = "production" @knife.config[:hide_healthy] = true expect(@query).to receive(:search).with(:node, "chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts) @knife.run end it "should not use partial search with long output" do @knife.config[:long_output] = true expect(@query).to receive(:search).with(:node, "*:*", {}) @knife.run end context "with a custom query" do before :each do @knife.instance_variable_set(:@name_args, ["name:my_custom_name"]) end it "should allow a custom query to be specified" do expect(@query).to receive(:search).with(:node, "name:my_custom_name", opts) @knife.run end it "should filter healthy nodes" do @knife.config[:hide_healthy] = true expect(@query).to receive(:search).with(:node, "name:my_custom_name NOT ohai_time:[1428569820 TO 1428573420]", opts) @knife.run end it "should filter by environment" do @knife.config[:environment] = "production" expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production", opts) @knife.run end it "should filter by environment and health" do @knife.config[:environment] = "production" @knife.config[:hide_healthy] = true expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts) @knife.run end end it "should not colorize output unless it's writing to a tty" do @knife.run expect(@stdout.string.match(/foobar/)).not_to be_nil expect(@stdout.string.match(/\e.*ago/)).to be_nil end end end chef-12.3.0/spec/unit/knife/client_edit_spec.rb0000644000004100000410000000237012520074675021373 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ClientEdit do before(:each) do @knife = Chef::Knife::ClientEdit.new @knife.name_args = [ 'adam' ] end describe 'run' do it 'should edit the client' do expect(@knife).to receive(:edit_object).with(Chef::ApiClient, 'adam') @knife.run end it 'should print usage and exit when a client name is not provided' do @knife.name_args = [] expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal) expect { @knife.run }.to raise_error(SystemExit) end end end chef-12.3.0/spec/unit/knife/configure_client_spec.rb0000644000004100000410000000602212520074675022425 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2011 Thomas Bishop # 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::ConfigureClient do before do @knife = Chef::Knife::ConfigureClient.new Chef::Config[:chef_server_url] = 'https://chef.example.com' Chef::Config[:validation_client_name] = 'chef-validator' Chef::Config[:validation_key] = '/etc/chef/validation.pem' @stderr = StringIO.new allow(@knife.ui).to receive(:stderr).and_return(@stderr) end describe 'run' do it 'should print usage and exit when a directory is not provided' do expect(@knife).to receive(:show_usage) expect(@knife.ui).to receive(:fatal).with(/must provide the directory/) expect { @knife.run }.to raise_error SystemExit end describe 'when specifing a directory' do before do @knife.name_args = ['/home/bob/.chef'] @client_file = StringIO.new @validation_file = StringIO.new expect(File).to receive(:open).with('/home/bob/.chef/client.rb', 'w'). and_yield(@client_file) expect(File).to receive(:open).with('/home/bob/.chef/validation.pem', 'w'). and_yield(@validation_file) expect(IO).to receive(:read).and_return('foo_bar_baz') end it 'should recursively create the directory' do expect(FileUtils).to receive(:mkdir_p).with('/home/bob/.chef') @knife.run end it 'should write out the config file' do allow(FileUtils).to receive(:mkdir_p) @knife.run expect(@client_file.string).to match /log_level\s+\:info/ expect(@client_file.string).to match /log_location\s+STDOUT/ expect(@client_file.string).to match /chef_server_url\s+'https\:\/\/chef\.example\.com'/ expect(@client_file.string).to match /validation_client_name\s+'chef-validator'/ end it 'should write out the validation.pem file' do allow(FileUtils).to receive(:mkdir_p) @knife.run expect(@validation_file.string).to match /foo_bar_baz/ end it 'should print information on what is being configured' do allow(FileUtils).to receive(:mkdir_p) @knife.run expect(@stderr.string).to match /creating client configuration/i expect(@stderr.string).to match /writing client\.rb/i expect(@stderr.string).to match /writing validation\.pem/i end end end end chef-12.3.0/spec/unit/log_spec.rb0000644000004100000410000000137612520074675016602 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'tempfile' require 'logger' require 'spec_helper' describe Chef::Log do end chef-12.3.0/spec/unit/client_spec.rb0000644000004100000410000006670212520074675017303 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Copyright:: Copyright 2008-2010 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/run_context' require 'chef/rest' require 'rbconfig' class FooError < RuntimeError end describe Chef::Client do let(:hostname) { "hostname" } let(:machinename) { "machinename.example.org" } let(:fqdn) { "hostname.example.org" } let(:ohai_data) do { :fqdn => fqdn, :hostname => hostname, :machinename => machinename, :platform => 'example-platform', :platform_version => 'example-platform-1.0', :data => {} } end let(:ohai_system) do ohai_system = double( "Ohai::System", :all_plugins => true, :data => ohai_data) allow(ohai_system).to receive(:[]) do |key| ohai_data[key] end ohai_system end let(:node) do Chef::Node.new.tap do |n| n.name(fqdn) n.chef_environment("_default") end end let(:json_attribs) { nil } let(:client_opts) { {} } let(:client) do Chef::Config[:event_loggers] = [] Chef::Client.new(json_attribs, client_opts).tap do |c| c.node = node end end before do Chef::Log.logger = Logger.new(StringIO.new) # Node/Ohai data #Chef::Config[:node_name] = fqdn allow(Ohai::System).to receive(:new).and_return(ohai_system) end context "when minimal ohai is configured" do before do Chef::Config[:minimal_ohai] = true end it "runs ohai with only the minimum required plugins" do expected_filter = %w[fqdn machinename hostname platform platform_version os os_version] expect(ohai_system).to receive(:all_plugins).with(expected_filter) client.run_ohai end end describe "authentication protocol selection" do after do Chef::Config[:authentication_protocol_version] = "1.0" end context "when the node name is <= 90 bytes" do it "does not force the authentication protocol to 1.1" do Chef::Config[:node_name] = ("f" * 90) # ugly that this happens as a side effect of a getter :( client.node_name expect(Chef::Config[:authentication_protocol_version]).to eq("1.0") end end context "when the node name is > 90 bytes" do it "sets the authentication protocol to version 1.1" do Chef::Config[:node_name] = ("f" * 91) # ugly that this happens as a side effect of a getter :( client.node_name expect(Chef::Config[:authentication_protocol_version]).to eq("1.1") end end end describe "configuring output formatters" do context "when no formatter has been configured" do context "and STDOUT is a TTY" do before do allow(STDOUT).to receive(:tty?).and_return(true) end it "configures the :doc formatter" do expect(client.formatters_for_run).to eq([[:doc]]) end context "and force_logger is set" do before do Chef::Config[:force_logger] = true end it "configures the :null formatter" do expect(Chef::Config[:force_logger]).to be_truthy expect(client.formatters_for_run).to eq([[:null]]) end end end context "and STDOUT is not a TTY" do before do allow(STDOUT).to receive(:tty?).and_return(false) end it "configures the :null formatter" do expect(client.formatters_for_run).to eq([[:null]]) end context "and force_formatter is set" do before do Chef::Config[:force_formatter] = true end it "it configures the :doc formatter" do expect(client.formatters_for_run).to eq([[:doc]]) end end end end context "when a formatter is configured" do context "with no output path" do before do Chef::Config.add_formatter(:min) end it "does not configure a default formatter" do expect(client.formatters_for_run).to eq([[:min, nil]]) end it "configures the formatter for STDOUT/STDERR" do configured_formatters = client.configure_formatters min_formatter = configured_formatters[0] expect(min_formatter.output.out).to eq(STDOUT) expect(min_formatter.output.err).to eq(STDERR) end end context "with an output path" do before do @tmpout = Tempfile.open("rspec-for-client-formatter-selection-#{Process.pid}") Chef::Config.add_formatter(:min, @tmpout.path) end after do @tmpout.close unless @tmpout.closed? @tmpout.unlink end it "configures the formatter for the file path" do configured_formatters = client.configure_formatters min_formatter = configured_formatters[0] expect(min_formatter.output.out.path).to eq(@tmpout.path) expect(min_formatter.output.err.path).to eq(@tmpout.path) end end end end describe "a full client run" do shared_context "a client run" do let(:http_node_load) { double("Chef::REST (node)") } let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") } let(:http_node_save) { double("Chef::REST (node save)") } let(:runner) { double("Chef::Runner") } let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => false) } let(:api_client_exists?) { false } let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:enable_fork) { false } def stub_for_register # --Client.register # Make sure Client#register thinks the client key doesn't # exist, so it tries to register and create one. allow(File).to receive(:exists?).and_call_original expect(File).to receive(:exists?). with(Chef::Config[:client_key]). exactly(:once). and_return(api_client_exists?) unless api_client_exists? # Client.register will register with the validation client name. expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run) end end def stub_for_node_load # Client.register will then turn around create another # Chef::REST object, this time with the client key it got from the # previous step. expect(Chef::REST).to receive(:new). with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]). exactly(:once). and_return(http_node_load) # --Client#build_node # looks up the node, which we will return, then later saves it. expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node) # --ResourceReporter#node_load_completed # gets a run id from the server for storing resource history # (has its own tests, so stubbing it here.) expect_any_instance_of(Chef::ResourceReporter).to receive(:node_load_completed) end def stub_for_sync_cookbooks # --Client#setup_run_context # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync # expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks) expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync) expect(http_cookbook_sync).to receive(:post). with("environments/_default/cookbook_versions", {:run_list => []}). and_return({}) end def stub_for_converge # --Client#converge expect(Chef::Runner).to receive(:new).and_return(runner) expect(runner).to receive(:converge).and_return(true) end def stub_for_audit # -- Client#run_audits expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) expect(audit_runner).to receive(:run).and_return(true) end def stub_for_node_save allow(node).to receive(:data_for_save).and_return(node.for_json) # --Client#save_updated_node expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_node_save) expect(http_node_save).to receive(:put_rest).with("nodes/#{fqdn}", node.for_json).and_return(true) end def stub_for_run expect_any_instance_of(Chef::RunLock).to receive(:acquire) expect_any_instance_of(Chef::RunLock).to receive(:save_pid) expect_any_instance_of(Chef::RunLock).to receive(:release) # Post conditions: check that node has been filled in correctly expect(client).to receive(:run_started) expect(client).to receive(:run_completed_successfully) # --ResourceReporter#run_completed # updates the server with the resource history # (has its own tests, so stubbing it here.) expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed) # --AuditReporter#run_completed # posts the audit data to server. # (has its own tests, so stubbing it here.) expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_completed) end before do Chef::Config[:client_fork] = enable_fork Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef' Chef::Config[:why_run] = false Chef::Config[:audit_mode] = :enabled stub_const("Chef::Client::STDOUT_FD", stdout) stub_const("Chef::Client::STDERR_FD", stderr) stub_for_register stub_for_node_load stub_for_sync_cookbooks stub_for_converge stub_for_audit stub_for_node_save stub_for_run end end shared_examples_for "a successful client run" do include_context "a client run" it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do # This is what we're testing. client.run # fork is stubbed, so we can see the outcome of the run expect(node.automatic_attrs[:platform]).to eq("example-platform") expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0") end end describe "when running chef-client without fork" do include_examples "a successful client run" end describe "when the client key already exists" do let(:api_client_exists?) { true } include_examples "a successful client run" end describe "when an override run list is given" do let(:client_opts) { {:override_runlist => "recipe[override_recipe]"} } it "should permit spaces in overriding run list" do Chef::Client.new(nil, :override_runlist => 'role[a], role[b]') end describe "when running the client" do include_examples "a successful client run" do before do # Client will try to compile and run override_recipe expect_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:compile) end def stub_for_sync_cookbooks # --Client#setup_run_context # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync # expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks) expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync) expect(http_cookbook_sync).to receive(:post). with("environments/_default/cookbook_versions", {:run_list => ["override_recipe"]}). and_return({}) end def stub_for_node_save # Expect NO node save expect(node).not_to receive(:save) end end end end describe "when a permanent run list is passed as an option" do include_examples "a successful client run" do let(:new_runlist) { "recipe[new_run_list_recipe]" } let(:client_opts) { {:runlist => new_runlist} } def stub_for_sync_cookbooks # --Client#setup_run_context # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync # expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks) expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync) expect(http_cookbook_sync).to receive(:post). with("environments/_default/cookbook_versions", {:run_list => ["new_run_list_recipe"]}). and_return({}) end before do # Client will try to compile and run the new_run_list_recipe, but we # do not create a fixture for this. expect_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:compile) end it "sets the new run list on the node" do client.run expect(node.run_list).to eq(Chef::RunList.new(new_runlist)) end end end describe "when converge fails" do include_context "a client run" do let(:e) { Exception.new } def stub_for_converge expect(Chef::Runner).to receive(:new).and_return(runner) expect(runner).to receive(:converge).and_raise(e) expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) end def stub_for_node_save expect(client).to_not receive(:save_updated_node) end def stub_for_run expect_any_instance_of(Chef::RunLock).to receive(:acquire) expect_any_instance_of(Chef::RunLock).to receive(:save_pid) expect_any_instance_of(Chef::RunLock).to receive(:release) # Post conditions: check that node has been filled in correctly expect(client).to receive(:run_started) expect(client).to receive(:run_failed) expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed) end end it "runs the audits and raises the error" do expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| expect(error.wrapped_errors.size).to eq(1) expect(error.wrapped_errors[0]).to eq(e) end end end describe "when the audit phase fails" do context "with an exception" do context "when audit mode is enabled" do include_context "a client run" do let(:e) { Exception.new } def stub_for_audit expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) expect(audit_runner).to receive(:run).and_raise(e) expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) end def stub_for_run expect_any_instance_of(Chef::RunLock).to receive(:acquire) expect_any_instance_of(Chef::RunLock).to receive(:save_pid) expect_any_instance_of(Chef::RunLock).to receive(:release) # Post conditions: check that node has been filled in correctly expect(client).to receive(:run_started) expect(client).to receive(:run_failed) expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed) end end it "should save the node after converge and raise exception" do expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| expect(error.wrapped_errors.size).to eq(1) expect(error.wrapped_errors[0]).to eq(e) end end end context "when audit mode is disabled" do include_context "a client run" do before do Chef::Config[:audit_mode] = :disabled end let(:e) { FooError.new } def stub_for_audit expect(Chef::Audit::Runner).to_not receive(:new) end def stub_for_converge expect(Chef::Runner).to receive(:new).and_return(runner) expect(runner).to receive(:converge).and_raise(e) expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(FooError) end def stub_for_node_save expect(client).to_not receive(:save_updated_node) end def stub_for_run expect_any_instance_of(Chef::RunLock).to receive(:acquire) expect_any_instance_of(Chef::RunLock).to receive(:save_pid) expect_any_instance_of(Chef::RunLock).to receive(:release) # Post conditions: check that node has been filled in correctly expect(client).to receive(:run_started) expect(client).to receive(:run_failed) expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) end it "re-raises an unwrapped exception" do expect { client.run }.to raise_error(FooError) end end end end context "with failed audits" do include_context "a client run" do let(:audit_runner) do instance_double("Chef::Audit::Runner", :run => true, :failed? => true, :num_failed => 1, :num_total => 1) end def stub_for_audit expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) end def stub_for_run expect_any_instance_of(Chef::RunLock).to receive(:acquire) expect_any_instance_of(Chef::RunLock).to receive(:save_pid) expect_any_instance_of(Chef::RunLock).to receive(:release) # Post conditions: check that node has been filled in correctly expect(client).to receive(:run_started) expect(client).to receive(:run_failed) expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed) expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed) end end it "should save the node after converge and raise exception" do expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| expect(error.wrapped_errors.size).to eq(1) expect(error.wrapped_errors[0]).to be_instance_of(Chef::Exceptions::AuditsFailed) end end end end describe "when why_run mode is enabled" do include_context "a client run" do before do Chef::Config[:why_run] = true end def stub_for_audit expect(Chef::Audit::Runner).to_not receive(:new) end def stub_for_node_save # This is how we should be mocking external calls - not letting it fall all the way through to the # REST call expect(node).to receive(:save) end it "runs successfully without enabling the audit runner" do client.run # fork is stubbed, so we can see the outcome of the run expect(node.automatic_attrs[:platform]).to eq("example-platform") expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0") end end end describe "when audits are disabled" do include_context "a client run" do before do Chef::Config[:audit_mode] = :disabled end def stub_for_audit expect(Chef::Audit::Runner).to_not receive(:new) end it "runs successfully without enabling the audit runner" do client.run # fork is stubbed, so we can see the outcome of the run expect(node.automatic_attrs[:platform]).to eq("example-platform") expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0") end end end end describe "when handling run failures" do it "should remove the run_lock on failure of #load_node" do @run_lock = double("Chef::RunLock", :acquire => true) allow(Chef::RunLock).to receive(:new).and_return(@run_lock) @events = double("Chef::EventDispatch::Dispatcher").as_null_object allow(Chef::EventDispatch::Dispatcher).to receive(:new).and_return(@events) # @events is created on Chef::Client.new, so we need to recreate it after mocking client = Chef::Client.new allow(client).to receive(:load_node).and_raise(Exception) expect(@run_lock).to receive(:release) expect { client.run }.to raise_error(Exception) end end describe "when notifying other objects of the status of the chef run" do before do Chef::Client.clear_notifications allow(Chef::Node).to receive(:find_or_create).and_return(node) allow(node).to receive(:save) client.load_node client.build_node end it "notifies observers that the run has started" do notified = false Chef::Client.when_run_starts do |run_status| expect(run_status.node).to eq(node) notified = true end client.run_started expect(notified).to be_truthy end it "notifies observers that the run has completed successfully" do notified = false Chef::Client.when_run_completes_successfully do |run_status| expect(run_status.node).to eq(node) notified = true end client.run_completed_successfully expect(notified).to be_truthy end it "notifies observers that the run failed" do notified = false Chef::Client.when_run_fails do |run_status| expect(run_status.node).to eq(node) notified = true end client.run_failed expect(notified).to be_truthy end end describe "build_node" do it "should expand the roles and recipes for the node" do node.run_list << "role[role_containing_cookbook1]" role_containing_cookbook1 = Chef::Role.new role_containing_cookbook1.name("role_containing_cookbook1") role_containing_cookbook1.run_list << "cookbook1" # build_node will call Node#expand! with server, which will # eventually hit the server to expand the included role. mock_chef_rest = double("Chef::REST") expect(mock_chef_rest).to receive(:get_rest).with("roles/role_containing_cookbook1").and_return(role_containing_cookbook1) expect(Chef::REST).to receive(:new).and_return(mock_chef_rest) # check pre-conditions. expect(node[:roles]).to be_nil expect(node[:recipes]).to be_nil allow(client.policy_builder).to receive(:node).and_return(node) # chefspec and possibly others use the return value of this method expect(client.build_node).to eq(node) # check post-conditions. expect(node[:roles]).not_to be_nil expect(node[:roles].length).to eq(1) expect(node[:roles]).to include("role_containing_cookbook1") expect(node[:recipes]).not_to be_nil expect(node[:recipes].length).to eq(1) expect(node[:recipes]).to include("cookbook1") end it "should set the environment from the specified configuration value" do expect(node.chef_environment).to eq("_default") Chef::Config[:environment] = "A" test_env = Chef::Environment.new test_env.name("A") mock_chef_rest = double("Chef::REST") expect(mock_chef_rest).to receive(:get_rest).with("environments/A").and_return(test_env) expect(Chef::REST).to receive(:new).and_return(mock_chef_rest) allow(client.policy_builder).to receive(:node).and_return(node) expect(client.build_node).to eq(node) expect(node.chef_environment).to eq("A") end end describe "windows_admin_check" do context "platform is not windows" do before do allow(Chef::Platform).to receive(:windows?).and_return(false) end it "shouldn't be called" do expect(client).not_to receive(:has_admin_privileges?) client.do_windows_admin_check end end context "platform is windows" do before do allow(Chef::Platform).to receive(:windows?).and_return(true) end it "should be called" do expect(client).to receive(:has_admin_privileges?) client.do_windows_admin_check end context "admin privileges exist" do before do expect(client).to receive(:has_admin_privileges?).and_return(true) end it "should not log a warning message" do expect(Chef::Log).not_to receive(:warn) client.do_windows_admin_check end context "fatal admin check is configured" do it "should not raise an exception" do client.do_windows_admin_check #should not raise end end end context "admin privileges doesn't exist" do before do expect(client).to receive(:has_admin_privileges?).and_return(false) end it "should log a warning message" do expect(Chef::Log).to receive(:warn) client.do_windows_admin_check end context "fatal admin check is configured" do it "should raise an exception" do client.do_windows_admin_check # should not raise end end end end end describe "assert_cookbook_path_not_empty" do before do Chef::Config[:solo] = true Chef::Config[:cookbook_path] = ["/path/to/invalid/cookbook_path"] end context "when any directory of cookbook_path contains no cookbook" do it "raises CookbookNotFound error" do expect do client.send(:assert_cookbook_path_not_empty, nil) end.to raise_error(Chef::Exceptions::CookbookNotFound, 'None of the cookbook paths set in Chef::Config[:cookbook_path], ["/path/to/invalid/cookbook_path"], contain any cookbooks') end end end describe "setting node name" do context "when machinename, hostname and fqdn are all set" do it "favors the fqdn" do expect(client.node_name).to eql(fqdn) end end context "when fqdn is missing" do # ohai 7 should always have machinename == return of hostname let(:fqdn) { nil } it "favors the machinename" do expect(client.node_name).to eql(machinename) end end context "when fqdn and machinename are missing" do # ohai 6 will not have machinename, return the short hostname let(:fqdn) { nil } let(:machinename) { nil } it "falls back to hostname" do expect(client.node_name).to eql(hostname) end end context "when they're all missing" do let(:machinename) { nil } let(:hostname) { nil } let(:fqdn) { nil } it "throws an exception" do expect { client.node_name }.to raise_error(Chef::Exceptions::CannotDetermineNodeName) end end end end chef-12.3.0/spec/unit/cookbook_spec.rb0000644000004100000410000000641712520074675017630 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' describe Chef::CookbookVersion do # COOKBOOK_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap")) before(:each) do @cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks")) cl = Chef::CookbookLoader.new(@cookbook_repo) cl.load_cookbooks @cookbook_collection = Chef::CookbookCollection.new(cl) @cookbook = @cookbook_collection[:openldap] @node = Chef::Node.new @node.name "JuliaChild" @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) end it "should have a name" do expect(@cookbook.name).to eq(:openldap) end it "should allow you to set the list of attribute files and create the mapping from short names to paths" do @cookbook.attribute_filenames = [ "attributes/one.rb", "attributes/two.rb" ] expect(@cookbook.attribute_filenames).to eq([ "attributes/one.rb", "attributes/two.rb" ]) expect(@cookbook.attribute_filenames_by_short_filename.keys.sort).to eql(["one", "two"]) expect(@cookbook.attribute_filenames_by_short_filename["one"]).to eq("attributes/one.rb") expect(@cookbook.attribute_filenames_by_short_filename["two"]).to eq("attributes/two.rb") end it "should allow you to set the list of recipe files and create the mapping of recipe short name to filename" do @cookbook.recipe_filenames = [ "recipes/one.rb", "recipes/two.rb" ] expect(@cookbook.recipe_filenames).to eq([ "recipes/one.rb", "recipes/two.rb" ]) expect(@cookbook.recipe_filenames_by_name.keys.sort).to eql(["one", "two"]) expect(@cookbook.recipe_filenames_by_name["one"]).to eq("recipes/one.rb") expect(@cookbook.recipe_filenames_by_name["two"]).to eq("recipes/two.rb") end it "should generate a list of recipes by fully-qualified name" do @cookbook.recipe_filenames = [ "recipes/one.rb", "/recipes/two.rb", "three.rb" ] expect(@cookbook.fully_qualified_recipe_names.include?("openldap::one")).to eq(true) expect(@cookbook.fully_qualified_recipe_names.include?("openldap::two")).to eq(true) expect(@cookbook.fully_qualified_recipe_names.include?("openldap::three")).to eq(true) end it "should find a preferred file" do skip end it "should not return an unchanged preferred file" do pending expect(@cookbook.preferred_filename(@node, :files, 'a-filename', 'the-checksum')).to be_nil end it "should raise an ArgumentException if you try to load a bad recipe name" do expect { @cookbook.load_recipe("doesnt_exist", @node) }.to raise_error(ArgumentError) end end chef-12.3.0/spec/unit/policy_builder_spec.rb0000644000004100000410000000145212520074675021021 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/policy_builder' describe Chef::PolicyBuilder do # TODO: test the strategy method end chef-12.3.0/spec/unit/dsl/0000755000004100000410000000000012520074675015235 5ustar www-datawww-datachef-12.3.0/spec/unit/dsl/regsitry_helper_spec.rb0000644000004100000410000000403612520074675022006 0ustar www-datawww-data# # Author:: Prajakta Purohit () # 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 "chef/dsl/registry_helper" require "spec_helper" describe Chef::Resource::RegistryKey do before (:all) do events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins node.consume_external_attrs(ohai.data,{}) run_context = Chef::RunContext.new(node, {}, events) @resource = Chef::Resource::new("foo", run_context) end context "tests registry dsl" do it "resource can access registry_helper method registry_key_exists" do expect(@resource.respond_to?('registry_key_exists?')).to eq(true) end it "resource can access registry_helper method registry_get_values" do expect(@resource.respond_to?('registry_get_values')).to eq(true) end it "resource can access registry_helper method registry_has_subkey" do expect(@resource.respond_to?('registry_has_subkeys?')).to eq(true) end it "resource can access registry_helper method registry_get_subkeys" do expect(@resource.respond_to?('registry_get_subkeys')).to eq(true) end it "resource can access registry_helper method registry_value_exists" do expect(@resource.respond_to?('registry_value_exists?')).to eq(true) end it "resource can access registry_helper method data_value_exists" do expect(@resource.respond_to?('registry_data_exists?')).to eq(true) end end end chef-12.3.0/spec/unit/dsl/platform_introspection_spec.rb0000644000004100000410000001200512520074675023376 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright (c) 2010 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/dsl/platform_introspection' class LanguageTester attr_reader :node def initialize(node) @node = node end include Chef::DSL::PlatformIntrospection end describe "PlatformIntrospection implementors" do let(:node) { Chef::Node.new } let(:platform_introspector) { LanguageTester.new(node) } it_behaves_like "a platform introspector" end describe Chef::DSL::PlatformIntrospection::PlatformDependentValue do before do platform_hash = { :openbsd => {:default => 'free, functional, secure'}, [:redhat, :centos, :fedora, :scientific] => {:default => '"stable"'}, :ubuntu => {'10.04' => 'using upstart more', :default => 'using init more'}, :default => 'bork da bork' } @platform_specific_value = Chef::DSL::PlatformIntrospection::PlatformDependentValue.new(platform_hash) end it "returns the default value when the platform doesn't match" do expect(@platform_specific_value.value_for_node(:platform => :dos)).to eq('bork da bork') end it "returns a value for a platform set as a group" do expect(@platform_specific_value.value_for_node(:platform => :centos)).to eq('"stable"') end it "returns a value for the platform when it was set as a symbol but fetched as a string" do expect(@platform_specific_value.value_for_node(:platform => "centos")).to eq('"stable"') end it "returns a value for a specific platform version" do node = {:platform => 'ubuntu', :platform_version => '10.04'} expect(@platform_specific_value.value_for_node(node)).to eq('using upstart more') end it "returns a platform-default value if the platform version doesn't match an explicit one" do node = {:platform => 'ubuntu', :platform_version => '9.10' } expect(@platform_specific_value.value_for_node(node)).to eq('using init more') end it "returns nil if there is no default and no platforms match" do # this matches the behavior in the original implementation. # whether or not it's correct is another matter. platform_specific_value = Chef::DSL::PlatformIntrospection::PlatformDependentValue.new({}) expect(platform_specific_value.value_for_node(:platform => 'foo')).to be_nil end it "raises an argument error if the platform hash is not correctly structured" do bad_hash = {:ubuntu => :foo} # should be :ubuntu => {:default => 'foo'} expect {Chef::DSL::PlatformIntrospection::PlatformDependentValue.new(bad_hash)}.to raise_error(ArgumentError) end end describe Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue do before do @array_values = [:stop, :start, :reload] @platform_family_hash = { "debian" => "debian value", [:rhel, "fedora"] => "redhatty value", "suse" => @array_values, :gentoo => "gentoo value", :default => "default value" } @platform_family_value = Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue.new(@platform_family_hash) end it "returns the default value when the platform family doesn't match" do expect(@platform_family_value.value_for_node(:platform_family => :os2)).to eq('default value') end it "returns a value for the platform family when it was set as a string but fetched as a symbol" do expect(@platform_family_value.value_for_node(:platform_family => :debian)).to eq("debian value") end it "returns a value for the platform family when it was set as a symbol but fetched as a string" do expect(@platform_family_value.value_for_node(:platform_family => "gentoo")).to eq("gentoo value") end it "returns an array value stored for a platform family" do expect(@platform_family_value.value_for_node(:platform_family => "suse")).to eq(@array_values) end it "returns a value for the platform family when it was set within an array hash key as a symbol" do expect(@platform_family_value.value_for_node(:platform_family => :rhel)).to eq("redhatty value") end it "returns a value for the platform family when it was set within an array hash key as a string" do expect(@platform_family_value.value_for_node(:platform_family => "fedora")).to eq("redhatty value") end it "returns nil if there is no default and no platforms match" do platform_specific_value = Chef::DSL::PlatformIntrospection::PlatformFamilyDependentValue.new({}) expect(platform_specific_value.value_for_node(:platform_family => 'foo')).to be_nil end end chef-12.3.0/spec/unit/dsl/data_query_spec.rb0000644000004100000410000000657412520074675020746 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright (c) 2010 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/dsl/data_query' class DataQueryDSLTester include Chef::DSL::DataQuery end describe Chef::DSL::DataQuery do let(:node) { Hash.new } let(:language) do language = DataQueryDSLTester.new allow(language).to receive(:node).and_return(@node) language end describe "::data_bag" do it "lists the items in a data bag" do allow(Chef::DataBag).to receive(:load) .with("bag_name") .and_return("item_1" => "http://url_for/item_1", "item_2" => "http://url_for/item_2") expect( language.data_bag("bag_name").sort ).to eql %w(item_1 item_2) end end shared_examples_for "a data bag item" do it "validates the name of the data bag you're trying to load an item from" do expect{ language.send(method_name, " %%^& ", "item_name") }.to raise_error(Chef::Exceptions::InvalidDataBagName) end it "validates the id of the data bag item you're trying to load" do expect{ language.send(method_name, "bag_name", " 987 (*&()") }.to raise_error(Chef::Exceptions::InvalidDataBagItemID) end it "validates that the id of the data bag item is not nil" do expect{ language.send(method_name, "bag_name", nil) }.to raise_error(Chef::Exceptions::InvalidDataBagItemID) end end describe "::data_bag_item" do let(:bag_name) { "bag_name" } let(:item_name) { "item_name" } let(:raw_data) {{ "id" => item_name, "greeting" => "hello", "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } } }} let(:item) do item = Chef::DataBagItem.new item.data_bag(bag_name) item.raw_data = raw_data item end it "fetches a data bag item" do allow( Chef::DataBagItem ).to receive(:load).with(bag_name, item_name).and_return(item) expect( language.data_bag_item(bag_name, item_name) ).to eql item end include_examples "a data bag item" do let(:method_name) { :data_bag_item } end context "when the item is encrypted" do let(:secret) { "abc123SECRET" } let(:enc_data_bag) { double("Chef::EncryptedDataBagItem") } before do allow( Chef::DataBagItem ).to receive(:load).with(bag_name, item_name).and_return(item) expect(language).to receive(:encrypted?).and_return(true) expect( Chef::EncryptedDataBagItem ).to receive(:load_secret).and_return(secret) end it "detects encrypted data bag" do expect( Chef::EncryptedDataBagItem ).to receive(:new).with(raw_data, secret).and_return(enc_data_bag) expect( Chef::Log ).to receive(:debug).with(/Data bag item looks encrypted/) expect(language.data_bag_item(bag_name, item_name)).to eq(enc_data_bag) end end end end chef-12.3.0/spec/unit/dsl/reboot_pending_spec.rb0000644000004100000410000001023112520074675021567 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require "chef/dsl/reboot_pending" require "spec_helper" describe Chef::DSL::RebootPending do describe "reboot_pending?" do describe "in isolation" do let(:recipe) { Object.new.extend(Chef::DSL::RebootPending) } before do allow(recipe).to receive(:platform?).and_return(false) end context "platform is windows" do before do allow(recipe).to receive(:platform?).with('windows').and_return(true) allow(recipe).to receive(:registry_key_exists?).and_return(false) allow(recipe).to receive(:registry_value_exists?).and_return(false) end it 'should return true if "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations" exists' do allow(recipe).to receive(:registry_value_exists?).with('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }).and_return(true) expect(recipe.reboot_pending?).to be_truthy end it 'should return true if "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" exists' do allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired').and_return(true) expect(recipe.reboot_pending?).to be_truthy end it 'should return true if key "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired" exists' do allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired').and_return(true) expect(recipe.reboot_pending?).to be_truthy end it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data' do allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true) allow(recipe).to receive(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return( [{:name => "Flags", :type => :dword, :data => 3}]) expect(recipe.reboot_pending?).to be_truthy end end context "platform is ubuntu" do before do allow(recipe).to receive(:platform?).with('ubuntu').and_return(true) end it 'should return true if /var/run/reboot-required exists' do allow(File).to receive(:exists?).with('/var/run/reboot-required').and_return(true) expect(recipe.reboot_pending?).to be_truthy end it 'should return false if /var/run/reboot-required does not exist' do allow(File).to receive(:exists?).with('/var/run/reboot-required').and_return(false) expect(recipe.reboot_pending?).to be_falsey end end end # describe in isolation describe "in a recipe" do it "responds to reboot_pending?" do # Chef::Recipe.new(cookbook_name, recipe_name, run_context(node, cookbook_collection, events)) recipe = Chef::Recipe.new(nil,nil,Chef::RunContext.new(Chef::Node.new, {}, nil)) expect(recipe).to respond_to(:reboot_pending?) end end # describe in a recipe describe "in a resource" do it "responds to reboot_pending?" do resource = Chef::Resource::new("Crackerjack::Timing", nil) expect(resource).to respond_to(:reboot_pending?) end end # describe in a resource end end chef-12.3.0/spec/unit/dsl/recipe_spec.rb0000644000004100000410000000447212520074675020052 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/dsl/recipe' RecipeDSLExampleClass = Struct.new(:cookbook_name, :recipe_name) class RecipeDSLExampleClass include Chef::DSL::Recipe end RecipeDSLBaseAPI = Struct.new(:cookbook_name, :recipe_name) class RecipeDSLExampleSubclass < RecipeDSLBaseAPI include Chef::DSL::Recipe end # TODO: most of DSL::Recipe's implementation is tested in Chef::Recipe's tests, # move those to here. describe Chef::DSL::Recipe do let(:cookbook_name) { "example_cb" } let(:recipe_name) { "example_recipe" } shared_examples_for "A Recipe DSL Implementation" do it "responds to cookbook_name" do expect(recipe.cookbook_name).to eq(cookbook_name) end it "responds to recipe_name" do expect(recipe.recipe_name).to eq(recipe_name) end it "responds to shell_out" do expect(recipe.respond_to?(:shell_out)).to be true end it "responds to shell_out" do expect(recipe.respond_to?(:shell_out!)).to be true end it "responds to shell_out" do expect(recipe.respond_to?(:shell_out_with_systems_locale)).to be true end end context "when included in a class that defines the required interface directly" do let(:recipe) { RecipeDSLExampleClass.new(cookbook_name, recipe_name) } include_examples "A Recipe DSL Implementation" end # This is the situation that occurs when the Recipe DSL gets mixed in to a # resource, for example. context "when included in a class that defines the required interface in a superclass" do let(:recipe) { RecipeDSLExampleSubclass.new(cookbook_name, recipe_name) } include_examples "A Recipe DSL Implementation" end end chef-12.3.0/spec/unit/dsl/audit_spec.rb0000644000004100000410000000252112520074675017702 0ustar www-datawww-data require 'spec_helper' require 'chef/dsl/audit' class AuditDSLTester < Chef::Recipe include Chef::DSL::Audit end class BadAuditDSLTester include Chef::DSL::Audit end describe Chef::DSL::Audit do let(:auditor) { AuditDSLTester.new("cookbook_name", "recipe_name", run_context) } let(:run_context) { instance_double(Chef::RunContext, :audits => audits, :cookbook_collection => cookbook_collection) } let(:audits) { {} } let(:cookbook_collection) { {} } it "raises an error when a block of audits is not provided" do expect{ auditor.control_group "name" }.to raise_error(Chef::Exceptions::NoAuditsProvided) end it "raises an error when no audit name is given" do expect{ auditor.control_group do end }.to raise_error(Chef::Exceptions::AuditNameMissing) end context "audits already populated" do let(:audits) { {"unique" => {} } } it "raises an error if the audit name is a duplicate" do expect { auditor.control_group "unique" do end }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate) end end context "included in a class without recipe DSL" do let(:auditor) { BadAuditDSLTester.new } it "fails because it relies on the recipe DSL existing" do expect { auditor.control_group "unique" do end }.to raise_error(NoMethodError, /undefined method `cookbook_name'/) end end end chef-12.3.0/spec/unit/handler_spec.rb0000644000004100000410000001507212520074675017434 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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::Handler do before(:each) do @handler = Chef::Handler.new @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_status = Chef::RunStatus.new(@node, @events) @handler.instance_variable_set(:@run_status, @run_status) end describe "when accessing the run status" do before do @backtrace = caller @exception = Exception.new("epic_fail") @exception.set_backtrace(@backtrace) @run_status.exception = @exception @run_context = Chef::RunContext.new(@node, {}, @events) @all_resources = [Chef::Resource::Cat.new('lolz'), Chef::Resource::ZenMaster.new('tzu')] @all_resources.first.updated = true @run_context.resource_collection.all_resources.replace(@all_resources) @run_status.run_context = @run_context @start_time = Time.now @end_time = @start_time + 4.2 allow(Time).to receive(:now).and_return(@start_time, @end_time) @run_status.start_clock @run_status.stop_clock end it "has a shortcut for the exception" do expect(@handler.exception).to eq(@exception) end it "has a shortcut for the backtrace" do expect(@handler.backtrace).to eq(@backtrace) end it "has a shortcut for all resources" do expect(@handler.all_resources).to eq(@all_resources) end it "has a shortcut for just the updated resources" do expect(@handler.updated_resources).to eq([@all_resources.first]) end it "has a shortcut for the start time" do expect(@handler.start_time).to eq(@start_time) end it "has a shortcut for the end time" do expect(@handler.end_time).to eq(@end_time) end it "has a shortcut for the elapsed time" do expect(@handler.elapsed_time).to eq(4.2) end it "has a shortcut for the node" do expect(@handler.node).to eq(@node) end it "has a shortcut for the run context" do expect(@handler.run_context).to eq(@run_context) end it "has a shortcut for the success? and failed? predicates" do expect(@handler.success?).to be_falsey # because there's an exception expect(@handler.failed?).to be_truthy end it "has a shortcut to the hash representation of the run status" do expect(@handler.data).to eq(@run_status.to_hash) end end describe "when running the report" do it "does not fail if the report handler raises an exception" do $report_ran = false def @handler.report $report_ran = true raise Exception, "I died the deth" end expect {@handler.run_report_safely(@run_status)}.not_to raise_error expect($report_ran).to be_truthy end it "does not fail if the report handler does not raise an exception" do $report_ran = false def @handler.report $report_ran = true end expect {@handler.run_report_safely(@run_status)}.not_to raise_error expect($report_ran).to be_truthy end end # Hmm, no tests for report handlers, looks like describe "when running a report handler" do before do @run_context = Chef::RunContext.new(@node, {}, @events) @all_resources = [Chef::Resource::Cat.new('foo'), Chef::Resource::ZenMaster.new('moo')] @all_resources.first.updated = true @run_context.resource_collection.all_resources.replace(@all_resources) @run_status.run_context = @run_context @start_time = Time.now @end_time = @start_time + 4.2 allow(Time).to receive(:now).and_return(@start_time, @end_time) @run_status.start_clock @run_status.stop_clock end it "has a shortcut for all resources" do expect(@handler.all_resources).to eq(@all_resources) end it "has a shortcut for just the updated resources" do expect(@handler.updated_resources).to eq([@all_resources.first]) end it "has a shortcut for the start time" do expect(@handler.start_time).to eq(@start_time) end it "has a shortcut for the end time" do expect(@handler.end_time).to eq(@end_time) end it "has a shortcut for the elapsed time" do expect(@handler.elapsed_time).to eq(4.2) end it "has a shortcut for the node" do expect(@handler.node).to eq(@node) end it "has a shortcut for the run context" do expect(@handler.run_context).to eq(@run_context) end it "has a shortcut for the success? and failed? predicates" do expect(@handler.success?).to be_truthy expect(@handler.failed?).to be_falsey end it "has a shortcut to the hash representation of the run status" do expect(@handler.data).to eq(@run_status.to_hash) end end # and this would test the start handler describe "when running a start handler" do before do @start_time = Time.now allow(Time).to receive(:now).and_return(@start_time) @run_status.start_clock end it "should not have all resources" do expect(@handler.all_resources).to be_falsey end it "should not have updated resources" do expect(@handler.updated_resources).to be_falsey end it "has a shortcut for the start time" do expect(@handler.start_time).to eq(@start_time) end it "does not have a shortcut for the end time" do expect(@handler.end_time).to be_falsey end it "does not have a shortcut for the elapsed time" do expect(@handler.elapsed_time).to be_falsey end it "has a shortcut for the node" do expect(@handler.node).to eq(@node) end it "does not have a shortcut for the run context" do expect(@handler.run_context).to be_falsey end it "has a shortcut for the success? and failed? predicates" do expect(@handler.success?).to be_truthy # for some reason this is true expect(@handler.failed?).to be_falsey end it "has a shortcut to the hash representation of the run status" do expect(@handler.data).to eq(@run_status.to_hash) end end end chef-12.3.0/spec/unit/application_spec.rb0000644000004100000410000004261612520074675020326 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Mark Mzyk (mmzyk@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' describe Chef::Application do before do @original_argv = ARGV.dup ARGV.clear Chef::Log.logger = Logger.new(StringIO.new) @app = Chef::Application.new allow(@app).to receive(:trap) allow(Dir).to receive(:chdir).and_return(0) allow(@app).to receive(:reconfigure) Chef::Log.init(STDERR) end after do ARGV.replace(@original_argv) end describe "reconfigure" do before do @app = Chef::Application.new allow(@app).to receive(:configure_chef).and_return(true) allow(@app).to receive(:configure_logging).and_return(true) allow(@app).to receive(:configure_proxy_environment_variables).and_return(true) end it "should configure chef" do expect(@app).to receive(:configure_chef).and_return(true) @app.reconfigure end it "should configure logging" do expect(@app).to receive(:configure_logging).and_return(true) @app.reconfigure end it "should configure environment variables" do expect(@app).to receive(:configure_proxy_environment_variables).and_return(true) @app.reconfigure end it 'should not receive set_specific_recipes' do expect(@app).to_not receive(:set_specific_recipes) @app.reconfigure end end describe Chef::Application do before do @app = Chef::Application.new end describe "run" do before do allow(@app).to receive(:setup_application).and_return(true) allow(@app).to receive(:run_application).and_return(true) allow(@app).to receive(:configure_chef).and_return(true) allow(@app).to receive(:configure_logging).and_return(true) end it "should reconfigure the application before running" do expect(@app).to receive(:reconfigure).and_return(true) @app.run end it "should setup the application before running it" do expect(@app).to receive(:setup_application).and_return(true) @app.run end it "should run the actual application" do expect(@app).to receive(:run_application).and_return(true) @app.run end end end describe "configure_chef" do before do # Silence warnings when no config file exists allow(Chef::Log).to receive(:warn) @app = Chef::Application.new #Chef::Config.stub(:merge!).and_return(true) allow(@app).to receive(:parse_options).and_return(true) end it "should parse the commandline options" do expect(@app).to receive(:parse_options).and_return(true) @app.config[:config_file] = "/etc/chef/default.rb" #have a config file set, to prevent triggering error block @app.configure_chef end describe "when a config_file is present" do let(:config_content) { "rspec_ran('true')" } let(:config_location) { "/etc/chef/default.rb" } let(:config_location_pathname) do p = Pathname.new(config_location) allow(p).to receive(:realpath).and_return(config_location) p end before do @app.config[:config_file] = config_location # force let binding to get evaluated or else we stub Pathname.new before we try to use it. config_location_pathname allow(Pathname).to receive(:new).with(config_location).and_return(config_location_pathname) expect(File).to receive(:read). with(config_location). and_return(config_content) end it "should configure chef::config from a file" do expect(Chef::Config).to receive(:from_string).with(config_content, config_location) @app.configure_chef end it "should merge the local config hash into chef::config" do #File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file) @app.configure_chef expect(Chef::Config.rspec_ran).to eq("true") end end describe "when there is no config_file defined" do before do @app.config[:config_file] = nil end it "should emit a warning" do expect(Chef::Config).not_to receive(:from_file).with("/etc/chef/default.rb") expect(Chef::Log).to receive(:warn).with("No config file found or specified on command line, using command line options.") @app.configure_chef end end describe "when the config file is set and not found" do before do @app.config[:config_file] = "/etc/chef/notfound" end it "should use the passed in command line options and defaults" do expect(Chef::Config).to receive(:merge!) @app.configure_chef end end end describe "when configuring the logger" do before do @app = Chef::Application.new allow(Chef::Log).to receive(:init) end it "should initialise the chef logger" do allow(Chef::Log).to receive(:level=) @monologger = double("Monologger") expect(MonoLogger).to receive(:new).with(Chef::Config[:log_location]).and_return(@monologger) expect(Chef::Log).to receive(:init).with(@monologger) @app.configure_logging end it "should raise fatals if log location is invalid" do Chef::Config[:log_location] = "/tmp/non-existing-dir/logfile" expect(Chef::Log).to receive(:fatal).at_least(:once) expect(Process).to receive(:exit) @app.configure_logging end shared_examples_for "log_level_is_auto" do context "when STDOUT is to a tty" do before do allow(STDOUT).to receive(:tty?).and_return(true) end it "configures the log level to :warn" do @app.configure_logging expect(Chef::Log.level).to eq(:warn) end context "when force_logger is configured" do before do Chef::Config[:force_logger] = true end it "configures the log level to info" do @app.configure_logging expect(Chef::Log.level).to eq(:info) end end end context "when STDOUT is not to a tty" do before do allow(STDOUT).to receive(:tty?).and_return(false) end it "configures the log level to :info" do @app.configure_logging expect(Chef::Log.level).to eq(:info) end context "when force_formatter is configured" do before do Chef::Config[:force_formatter] = true end it "sets the log level to :warn" do @app.configure_logging expect(Chef::Log.level).to eq(:warn) end end end end context "when log_level is not set" do it_behaves_like "log_level_is_auto" end context "when log_level is :auto" do before do Chef::Config[:log_level] = :auto end it_behaves_like "log_level_is_auto" end end describe "when configuring environment variables" do def configure_proxy_environment_variables_stubs allow(@app).to receive(:configure_http_proxy).and_return(true) allow(@app).to receive(:configure_https_proxy).and_return(true) allow(@app).to receive(:configure_ftp_proxy).and_return(true) allow(@app).to receive(:configure_no_proxy).and_return(true) end shared_examples_for "setting ENV['http_proxy']" do before do Chef::Config[:http_proxy] = http_proxy end it "should set ENV['http_proxy']" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://#{address}:#{port}") end it "should set ENV['HTTP_PROXY']" do @app.configure_proxy_environment_variables expect(@env['HTTP_PROXY']).to eq("#{scheme}://#{address}:#{port}") end describe "when Chef::Config[:http_proxy_user] is set" do before do Chef::Config[:http_proxy_user] = "username" end it "should set ENV['http_proxy'] with the username" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://username@#{address}:#{port}") expect(@env['HTTP_PROXY']).to eq("#{scheme}://username@#{address}:#{port}") end context "when :http_proxy_user contains '@' and/or ':'" do before do Chef::Config[:http_proxy_user] = "my:usern@me" end it "should set ENV['http_proxy'] with the escaped username" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://my%3Ausern%40me@#{address}:#{port}") expect(@env['HTTP_PROXY']).to eq("#{scheme}://my%3Ausern%40me@#{address}:#{port}") end end describe "when Chef::Config[:http_proxy_pass] is set" do before do Chef::Config[:http_proxy_pass] = "password" end it "should set ENV['http_proxy'] with the password" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://username:password@#{address}:#{port}") expect(@env['HTTP_PROXY']).to eq("#{scheme}://username:password@#{address}:#{port}") end context "when :http_proxy_pass contains '@' and/or ':'" do before do Chef::Config[:http_proxy_pass] = ":P@ssword101" end it "should set ENV['http_proxy'] with the escaped password" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://username:%3AP%40ssword101@#{address}:#{port}") expect(@env['HTTP_PROXY']).to eq("#{scheme}://username:%3AP%40ssword101@#{address}:#{port}") end end end end describe "when Chef::Config[:http_proxy_pass] is set (but not Chef::Config[:http_proxy_user])" do before do Chef::Config[:http_proxy_user] = nil Chef::Config[:http_proxy_pass] = "password" end it "should set ENV['http_proxy']" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq("#{scheme}://#{address}:#{port}") expect(@env['HTTP_PROXY']).to eq("#{scheme}://#{address}:#{port}") end end end describe "when configuring ENV['http_proxy']" do before do @env = {} allow(@app).to receive(:env).and_return(@env) allow(@app).to receive(:configure_https_proxy).and_return(true) allow(@app).to receive(:configure_ftp_proxy).and_return(true) allow(@app).to receive(:configure_no_proxy).and_return(true) end describe "when Chef::Config[:http_proxy] is not set" do before do Chef::Config[:http_proxy] = nil end it "should not set ENV['http_proxy']" do @app.configure_proxy_environment_variables expect(@env).to eq({}) end end describe "when Chef::Config[:http_proxy] is set" do context "when given an FQDN" do let(:scheme) { "http" } let(:address) { "proxy.example.org" } let(:port) { 8080 } let(:http_proxy) { "#{scheme}://#{address}:#{port}" } it_should_behave_like "setting ENV['http_proxy']" end context "when given an HTTPS URL" do let(:scheme) { "https" } let(:address) { "proxy.example.org" } let(:port) { 8080 } let(:http_proxy) { "#{scheme}://#{address}:#{port}" } it_should_behave_like "setting ENV['http_proxy']" end context "when given an IP" do let(:scheme) { "http" } let(:address) { "127.0.0.1" } let(:port) { 22 } let(:http_proxy) { "#{scheme}://#{address}:#{port}" } it_should_behave_like "setting ENV['http_proxy']" end context "when given an IPv6" do let(:scheme) { "http" } let(:address) { "[2001:db8::1]" } let(:port) { 80 } let(:http_proxy) { "#{scheme}://#{address}:#{port}" } it_should_behave_like "setting ENV['http_proxy']" end context "when given without including http://" do let(:scheme) { "http" } let(:address) { "proxy.example.org" } let(:port) { 8181 } let(:http_proxy) { "#{address}:#{port}" } it_should_behave_like "setting ENV['http_proxy']" end context "when given the full proxy in :http_proxy only" do before do Chef::Config[:http_proxy] = "http://username:password@proxy.example.org:2222" Chef::Config[:http_proxy_user] = nil Chef::Config[:http_proxy_pass] = nil end it "should set ENV['http_proxy']" do @app.configure_proxy_environment_variables expect(@env['http_proxy']).to eq(Chef::Config[:http_proxy]) end end context "when the config options aren't URI compliant" do it "raises Chef::Exceptions::BadProxyURI" do Chef::Config[:http_proxy] = "http://proxy.bad_example.org/:8080" expect { @app.configure_proxy_environment_variables }.to raise_error(Chef::Exceptions::BadProxyURI) end end end end end describe "class method: fatal!" do before do allow(STDERR).to receive(:puts).with("FATAL: blah").and_return(true) allow(Chef::Log).to receive(:fatal).and_return(true) allow(Process).to receive(:exit).and_return(true) end it "should log an error message to the logger" do expect(Chef::Log).to receive(:fatal).with("blah").and_return(true) Chef::Application.fatal! "blah" end describe "when an exit code is supplied" do it "should exit with the given exit code" do expect(Process).to receive(:exit).with(-100).and_return(true) Chef::Application.fatal! "blah", -100 end end describe "when an exit code is not supplied" do it "should exit with the default exit code" do expect(Process).to receive(:exit).with(-1).and_return(true) Chef::Application.fatal! "blah" end end end describe "setup_application" do before do @app = Chef::Application.new end it "should raise an error" do expect { @app.setup_application }.to raise_error(Chef::Exceptions::Application) end end describe "run_application" do before do @app = Chef::Application.new end it "should raise an error" do expect { @app.run_application }.to raise_error(Chef::Exceptions::Application) end end context "when the config file is not available" do it "should warn for bad config file path" do @app.config[:config_file] = "/tmp/non-existing-dir/file" config_file_regexp = Regexp.new @app.config[:config_file] expect(Chef::Log).to receive(:warn).at_least(:once).with(config_file_regexp).and_return(true) allow(Chef::Log).to receive(:warn).and_return(true) @app.configure_chef end end describe 'run_chef_client' do context 'with an application' do let(:app) { Chef::Application.new } context 'when called with an invalid argument' do before do allow(app).to receive(:fork_chef_client).and_return(true) allow(app).to receive(:run_with_graceful_exit_option).and_return(true) end it 'should raise an argument error detailing the problem' do specific_recipes_regexp = Regexp.new 'received non-Array like specific_recipes argument' expect { app.run_chef_client(nil) }.to raise_error(ArgumentError, specific_recipes_regexp) end end context 'when called with an Array-like argument (#size)' do before do allow(app).to receive(:fork_chef_client).and_return(true) allow(app).to receive(:run_with_graceful_exit_option).and_return(true) end it 'should be cool' do expect { app.run_chef_client([]) }.not_to raise_error end end end end describe "configuration errors" do before do expect(Process).to receive(:exit) end def raises_informative_fatals_on_configure_chef config_file_regexp = Regexp.new @app.config[:config_file] expect(Chef::Log).to receive(:fatal). with(/Configuration error/) expect(Chef::Log).to receive(:fatal). with(config_file_regexp). at_least(1).times @app.configure_chef end describe "when config file exists but contains errors" do def create_config_file(text) @config_file = Tempfile.new("rspec-chef-config") @config_file.write(text) @config_file.close @app.config[:config_file] = @config_file.path end after(:each) do @config_file.unlink end it "should raise informative fatals for badly written config" do create_config_file("text that should break the config parsing") raises_informative_fatals_on_configure_chef end end end end chef-12.3.0/spec/unit/http_spec.rb0000644000004100000410000000640312520074675016774 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2014 Onddo Labs, SL. # 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/http' require 'chef/http/basic_client' require 'chef/http/socketless_chef_zero_client' class Chef::HTTP public :create_url end describe Chef::HTTP do context "when given a chefzero:// URL" do let(:uri) { URI("chefzero://localhost:1") } subject(:http) { Chef::HTTP.new(uri) } it "uses the SocketlessChefZeroClient to handle requests" do expect(http.http_client).to be_a_kind_of(Chef::HTTP::SocketlessChefZeroClient) expect(http.http_client.url).to eq(uri) end end describe "create_url" do it 'should return a correctly formatted url 1/3 CHEF-5261' do http = Chef::HTTP.new('http://www.getchef.com') expect(http.create_url('api/endpoint')).to eql(URI.parse('http://www.getchef.com/api/endpoint')) end it 'should return a correctly formatted url 2/3 CHEF-5261' do http = Chef::HTTP.new('http://www.getchef.com/') expect(http.create_url('/organization/org/api/endpoint/')).to eql(URI.parse('http://www.getchef.com/organization/org/api/endpoint/')) end it 'should return a correctly formatted url 3/3 CHEF-5261' do http = Chef::HTTP.new('http://www.getchef.com/organization/org///') expect(http.create_url('///api/endpoint?url=http://foo.bar')).to eql(URI.parse('http://www.getchef.com/organization/org/api/endpoint?url=http://foo.bar')) end # As per: https://github.com/opscode/chef/issues/2500 it 'should treat scheme part of the URI in a case-insensitive manner' do http = Chef::HTTP.allocate # Calling Chef::HTTP::new sets @url, don't want that. expect { http.create_url('HTTP://www1.chef.io/') }.not_to raise_error expect(http.create_url('HTTP://www2.chef.io/')).to eql(URI.parse('http://www2.chef.io/')) end end # create_url describe "head" do it 'should return nil for a "200 Success" response (CHEF-4762)' do resp = Net::HTTPOK.new("1.1", 200, "OK") expect(resp).to receive(:read_body).and_return(nil) http = Chef::HTTP.new("") expect_any_instance_of(Chef::HTTP::BasicClient).to receive(:request).and_return(["request", resp]) expect(http.head("http://www.getchef.com/")).to eql(nil) end it 'should return false for a "304 Not Modified" response (CHEF-4762)' do resp = Net::HTTPNotModified.new("1.1", 304, "Not Modified") expect(resp).to receive(:read_body).and_return(nil) http = Chef::HTTP.new("") expect_any_instance_of(Chef::HTTP::BasicClient).to receive(:request).and_return(["request", resp]) expect(http.head("http://www.getchef.com/")).to eql(false) end end # head end chef-12.3.0/spec/unit/guard_interpreter_spec.rb0000644000004100000410000000331312520074675021537 0ustar www-datawww-data# # Author:: Steven Danna (steve@chef.io) # Copyright:: Copyright (c) 2015 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::GuardInterpreter do describe "#for_resource" do let (:resource) { Chef::Resource.new("foo")} it "returns a DefaultGuardInterpreter if the resource has guard_interpreter set to :default" do resource.guard_interpreter :default interpreter = Chef::GuardInterpreter.for_resource(resource, "", {}) expect(interpreter.class).to eq(Chef::GuardInterpreter::DefaultGuardInterpreter) end it "returns a ResourceGuardInterpreter if the resource has guard_interpreter set to !:default" do resource.guard_interpreter :foobar # Mock the resource guard interpreter to avoid having to set up a lot of state # currently we are only testing that we get the correct class of object back rgi = double("Chef::GuardInterpreter::ResourceGuardInterpreter") allow(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:new).and_return(rgi) interpreter = Chef::GuardInterpreter.for_resource(resource, "", {}) expect(interpreter).to eq(rgi) end end end chef-12.3.0/spec/unit/platform/0000755000004100000410000000000012520074675016277 5ustar www-datawww-datachef-12.3.0/spec/unit/platform/query_helpers_spec.rb0000644000004100000410000000517212520074675022532 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe "Chef::Platform#windows_server_2003?" do it "returns false early when not on windows" do allow(Chef::Platform).to receive(:windows?).and_return(false) expect(Chef::Platform).not_to receive(:require) expect(Chef::Platform.windows_server_2003?).to be_falsey end # CHEF-4888: Need to call WIN32OLE.ole_initialize in new threads it "does not raise an exception" do expect { Thread.fork { Chef::Platform.windows_server_2003? }.join }.not_to raise_error end end describe 'Chef::Platform#supports_dsc?' do it 'returns false if powershell is not present' do node = Chef::Node.new expect(Chef::Platform.supports_dsc?(node)).to be_falsey end ['1.0', '2.0', '3.0'].each do |version| it "returns false for Powershell #{version}" do node = Chef::Node.new node.automatic[:languages][:powershell][:version] = version expect(Chef::Platform.supports_dsc?(node)).to be_falsey end end ['4.0', '5.0'].each do |version| it "returns true for Powershell #{version}" do node = Chef::Node.new node.automatic[:languages][:powershell][:version] = version expect(Chef::Platform.supports_dsc?(node)).to be_truthy end end end describe 'Chef::Platform#supports_dsc_invoke_resource?' do it 'returns false if powershell is not present' do node = Chef::Node.new expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey end ['1.0', '2.0', '3.0', '4.0', '5.0.10017.9'].each do |version| it "returns false for Powershell #{version}" do node = Chef::Node.new node.automatic[:languages][:powershell][:version] = version expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey end end it "returns true for Powershell 5.0.10018.0" do node = Chef::Node.new node.automatic[:languages][:powershell][:version] = "5.0.10018.0" expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_truthy end end chef-12.3.0/spec/unit/digester_spec.rb0000644000004100000410000000325312520074675017623 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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::Digester do before(:each) do @cache = Chef::Digester.instance end describe "when computing checksums of cookbook files and templates" do it "proxies the class method checksum_for_file to the instance" do expect(@cache).to receive(:checksum_for_file).with("a_file_or_a_fail") Chef::Digester.checksum_for_file("a_file_or_a_fail") end it "computes a checksum of a file" do fixture_file = CHEF_SPEC_DATA + "/checksum/random.txt" expected = "09ee9c8cc70501763563bcf9c218d71b2fbf4186bf8e1e0da07f0f42c80a3394" expect(@cache.checksum_for_file(fixture_file)).to eq(expected) end it "generates a checksum from a non-file IO object" do io = StringIO.new("riseofthemachines\nriseofthechefs\n") expected_md5 = '0e157ac1e2dd73191b76067fb6b4bceb' expect(@cache.generate_md5_checksum(io)).to eq(expected_md5) end end end chef-12.3.0/spec/unit/run_lock_spec.rb0000644000004100000410000001131512520074675017627 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 File.expand_path('../../spec_helper', __FILE__) require 'chef/client' describe Chef::RunLock do default_cache_path = windows? ? 'C:\chef' : '/var/chef' default_pid_location = windows? ? 'C:\chef\cache\chef-client-running.pid' : '/var/chef/cache/chef-client-running.pid' describe "when first created" do it "locates the lockfile in the file cache path by default" do allow(Chef::Config).to receive(:cache_path).and_return(default_cache_path) run_lock = Chef::RunLock.new(Chef::Config.lockfile) expect(run_lock.runlock_file).to eq(default_pid_location) end it "locates the lockfile in the user-configured path when set" do Chef::Config.lockfile = "/tmp/chef-client-running.pid" run_lock = Chef::RunLock.new(Chef::Config.lockfile) expect(run_lock.runlock_file).to eq("/tmp/chef-client-running.pid") end end describe "acquire" do let(:lockfile) { "/tmp/chef-client-running.pid" } subject(:runlock) { Chef::RunLock.new(lockfile) } def stub_unblocked_run allow(runlock).to receive(:test).and_return(true) end def stub_blocked_run(duration) allow(runlock).to receive(:test).and_return(false) allow(runlock).to receive(:wait) { sleep(duration) } allow(runlock).to receive(:runpid).and_return(666) # errors read blocking pid end describe "when Chef::Config[:run_lock_timeout] is not set (set to default)" do describe "and the lockfile is not locked by another client run" do it "should not wait" do stub_unblocked_run expect_any_instance_of(Chef::RunLock).not_to receive(:wait) runlock.acquire end end describe "and the lockfile is locked by another client run" do it "should wait for the lock to be released" do stub_blocked_run(0.001) expect(runlock).to receive(:wait) runlock.acquire end end end describe "when Chef::Config[:run_lock_timeout] is set to 0" do before(:each) do @default_timeout = Chef::Config[:run_lock_timeout] Chef::Config[:run_lock_timeout] = 0 end after(:each) do Chef::Config[:run_lock_timeout] = @default_timeout end describe "and the lockfile is not locked by another client run" do it "should acquire the lock" do stub_unblocked_run expect(runlock).not_to receive(:wait) runlock.acquire end end describe "and the lockfile is locked by another client run" do it "should raise Chef::Exceptions::RunLockTimeout" do stub_blocked_run(0.001) expect(runlock).not_to receive(:wait) expect{ runlock.acquire }.to raise_error(Chef::Exceptions::RunLockTimeout) end end end describe "when Chef::Config[:run_lock_timeout] is set to >0" do before(:each) do @default_timeout = Chef::Config[:run_lock_timeout] @timeout = 0.1 Chef::Config[:run_lock_timeout] = @timeout end after(:each) do Chef::Config[:run_lock_timeout] = @default_timeout end describe "and the lockfile is not locked by another client run" do it "should acquire the lock" do stub_unblocked_run expect(runlock).not_to receive(:wait) runlock.acquire end end describe "and the lockfile is locked by another client run" do describe "and the lock is released before the timeout expires" do it "should acquire the lock" do stub_blocked_run(@timeout/2.0) expect(runlock).to receive(:wait) expect{ runlock.acquire }.not_to raise_error end end describe "and the lock is not released before the timeout expires" do it "should raise a RunLockTimeout exception" do stub_blocked_run(2.0) expect(runlock).to receive(:wait) expect{ runlock.acquire }.to raise_error(Chef::Exceptions::RunLockTimeout) end end end end end # See also: spec/functional/run_lock_spec end chef-12.3.0/spec/unit/run_status_spec.rb0000644000004100000410000001063112520074675020222 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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::RunStatus do before do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @run_status = Chef::RunStatus.new(@node, @events) end describe "before the run context has been set" do it "converts to a hash" do @run_status.to_hash end end describe "when the run context has been set" do before do @run_status.run_context = @run_context end it "has a run context" do expect(@run_status.run_context).to equal(@run_context) end it "provides access to the run context's node" do expect(@run_status.node).to equal(@node) end it "converts to a hash" do expect(@run_status.to_hash[:node]).to equal(@node) expect(@run_status.to_hash[:success]).to be_truthy end describe "after it has recorded timing information" do before do @start_time = Time.new @end_time = @start_time + 23 allow(Time).to receive(:now).and_return(@start_time, @end_time) @run_status.start_clock @run_status.stop_clock end it "records the start time of the run" do expect(@run_status.start_time).to eq(@start_time) end it "records the end time of the run" do expect(@run_status.end_time).to eq(@end_time) end it "gives the elapsed time of the chef run" do expect(@run_status.elapsed_time).to eq(23) end it "includes timing information in its hash form" do expect(@run_status.to_hash[:start_time]).to eq(@start_time) expect(@run_status.to_hash[:end_time]).to eq(@end_time) expect(@run_status.to_hash[:elapsed_time]).to eq(23) end end describe "with resources in the resource_collection" do before do @all_resources = [Chef::Resource::Cat.new("whiskers"), Chef::Resource::ZenMaster.new('dtz')] @run_context.resource_collection.all_resources.replace(@all_resources) end it "lists all resources" do expect(@run_status.all_resources).to eq(@all_resources) end it "has no updated resources" do expect(@run_status.updated_resources).to be_empty end it "includes the list of all resources in its hash form" do expect(@run_status.to_hash[:all_resources]).to eq(@all_resources) expect(@run_status.to_hash[:updated_resources]).to be_empty end describe "and some have been updated" do before do @all_resources.first.updated = true end it "lists the updated resources" do expect(@run_status.updated_resources).to eq([@all_resources.first]) end it "includes the list of updated resources in its hash form" do expect(@run_status.to_hash[:updated_resources]).to eq([@all_resources.first]) end end end describe "when the run failed" do before do @exception = Exception.new("just testing") @backtrace = caller @exception.set_backtrace(@backtrace) @run_status.exception = @exception end it "stores the exception" do expect(@run_status.exception).to equal(@exception) end it "stores the backtrace" do expect(@run_status.backtrace).to eq(@backtrace) end it "says the run was not successful" do expect(@run_status.success?).to be_falsey expect(@run_status.failed?).to be_truthy end it "converts to a hash including the exception information" do expect(@run_status.to_hash[:success]).to be_falsey expect(@run_status.to_hash[:exception]).to eq("Exception: just testing") expect(@run_status.to_hash[:backtrace]).to eq(@backtrace) end end end end chef-12.3.0/spec/unit/workstation_config_loader_spec.rb0000644000004100000410000002230712520074675023255 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'tempfile' require 'chef/workstation_config_loader' describe Chef::WorkstationConfigLoader do let(:explicit_config_location) { nil } let(:env) { {} } let(:config_loader) do described_class.new(explicit_config_location).tap do |c| allow(c).to receive(:env).and_return(env) end end # Test methods that do I/O or reference external state which are stubbed out # elsewhere. describe "external dependencies" do let(:config_loader) { described_class.new(nil) } it "delegates to ENV for env" do expect(config_loader.env).to equal(ENV) end it "tests a path's existence" do expect(config_loader.path_exists?('/nope/nope/nope/nope/frab/jab/nab')).to be(false) expect(config_loader.path_exists?(__FILE__)).to be(true) end end describe "locating the config file" do context "without an explicit config" do before do allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false) end it "has no config if HOME is not set" do expect(config_loader.config_location).to be(nil) expect(config_loader.no_config_found?).to be(true) end context "when HOME is set and contains a knife.rb" do let(:home) { "/Users/example.user" } before do allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef')) allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true) end it "uses the config in HOME/.chef/knife.rb" do expect(config_loader.config_location).to eq("#{home}/.chef/knife.rb") end context "and has a config.rb" do before do allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/config.rb").and_return(true) end it "uses the config in HOME/.chef/config.rb" do expect(config_loader.config_location).to eq("#{home}/.chef/config.rb") end context "and/or a parent dir contains a .chef dir" do let(:env_pwd) { "/path/to/cwd" } before do if Chef::Platform.windows? env["CD"] = env_pwd else env["PWD"] = env_pwd end allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/knife.rb").and_return(true) allow(File).to receive(:exist?).with("#{env_pwd}/.chef").and_return(true) allow(File).to receive(:directory?).with("#{env_pwd}/.chef").and_return(true) end it "prefers the config from parent_dir/.chef" do expect(config_loader.config_location).to eq("#{env_pwd}/.chef/knife.rb") end context "and the parent dir's .chef dir has a config.rb" do before do allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/config.rb").and_return(true) end it "prefers the config from parent_dir/.chef" do expect(config_loader.config_location).to eq("#{env_pwd}/.chef/config.rb") end context "and/or the current working directory contains a .chef dir" do let(:cwd) { Dir.pwd } before do allow(config_loader).to receive(:path_exists?).with("#{cwd}/knife.rb").and_return(true) end it "prefers a knife.rb located in the cwd" do expect(config_loader.config_location).to eq("#{cwd}/knife.rb") end context "and the CWD's .chef dir has a config.rb" do before do allow(config_loader).to receive(:path_exists?).with("#{cwd}/config.rb").and_return(true) end it "prefers a config located in the cwd" do expect(config_loader.config_location).to eq("#{cwd}/config.rb") end context "and/or KNIFE_HOME is set" do let(:knife_home) { "/path/to/knife/home" } before do env["KNIFE_HOME"] = knife_home allow(config_loader).to receive(:path_exists?).with("#{knife_home}/knife.rb").and_return(true) end it "prefers a knife located in KNIFE_HOME" do expect(config_loader.config_location).to eq("/path/to/knife/home/knife.rb") end context "and KNIFE_HOME contains a config.rb" do before do env["KNIFE_HOME"] = knife_home allow(config_loader).to receive(:path_exists?).with("#{knife_home}/config.rb").and_return(true) end it "prefers a config.rb located in KNIFE_HOME" do expect(config_loader.config_location).to eq("/path/to/knife/home/config.rb") end end end end end end end end end context "when the current working dir is inside a symlinked directory" do before do # pwd according to your shell is /home/someuser/prod/chef-repo, but # chef-repo is a symlink to /home/someuser/codes/chef-repo env["CD"] = "/home/someuser/prod/chef-repo" # windows env["PWD"] = "/home/someuser/prod/chef-repo" # unix allow(Dir).to receive(:pwd).and_return("/home/someuser/codes/chef-repo") end it "loads the config from the non-dereferenced directory path" do expect(File).to receive(:exist?).with("/home/someuser/prod/chef-repo/.chef").and_return(false) expect(File).to receive(:exist?).with("/home/someuser/prod/.chef").and_return(true) expect(File).to receive(:directory?).with("/home/someuser/prod/.chef").and_return(true) expect(config_loader).to receive(:path_exists?).with("/home/someuser/prod/.chef/knife.rb").and_return(true) expect(config_loader.config_location).to eq("/home/someuser/prod/.chef/knife.rb") end end end context "when given an explicit config to load" do let(:explicit_config_location) { "/path/to/explicit/config.rb" } it "prefers the explicit config" do expect(config_loader.config_location).to eq(explicit_config_location) end end end describe "loading the config file" do context "when no explicit config is specifed and no implicit config is found" do before do allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false) end it "skips loading" do expect(config_loader.config_location).to be(nil) expect(config_loader.load).to be(false) end end context "when an explicit config is given but it doesn't exist" do let(:explicit_config_location) { "/nope/nope/nope/frab/jab/nab" } it "raises a configuration error" do expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) end end context "when the config file exists" do let(:config_content) { "" } let(:explicit_config_location) do # could use described_class, but remove all ':' from the path if so. t = Tempfile.new("Chef-WorkstationConfigLoader-rspec-test") t.print(config_content) t.close t.path end after { File.unlink(explicit_config_location) if File.exists?(explicit_config_location) } context "and is valid" do let(:config_content) { "config_file_evaluated(true)" } it "loads the config" do expect(config_loader.load).to be(true) expect(Chef::Config.config_file_evaluated).to be(true) end it "sets Chef::Config.config_file" do config_loader.load expect(Chef::Config.config_file).to eq(explicit_config_location) end end context "and has a syntax error" do let(:config_content) { "{{{{{:{{" } it "raises a ConfigurationError" do expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) end end context "and raises a ruby exception during evaluation" do let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" } it "raises a ConfigurationError" do expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) end end end end end chef-12.3.0/spec/unit/shell/0000755000004100000410000000000012520074675015562 5ustar www-datawww-datachef-12.3.0/spec/unit/shell/shell_ext_spec.rb0000644000004100000410000001313112520074675021107 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # Copyright:: Copyright (c) 2010 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 Shell::Extensions do describe "extending object for top level methods" do before do @shell_client = TestableShellSession.instance allow(Shell).to receive(:session).and_return(@shell_client) @job_manager = TestJobManager.new @root_context = Object.new @root_context.instance_eval(&ObjectTestHarness) Shell::Extensions.extend_context_object(@root_context) @root_context.conf = double("irbconf") end it "finds a subsession in irb for an object" do target_context_obj = Chef::Node.new irb_context = double("context", :main => target_context_obj) irb_session = double("irb session", :context => irb_context) @job_manager.jobs = [[:thread, irb_session]] allow(@root_context).to receive(:jobs).and_return(@job_manager) @root_context.ensure_session_select_defined expect(@root_context.jobs.select_shell_session(target_context_obj)).to eq(irb_session) expect(@root_context.jobs.select_shell_session(:idontexist)).to be_nil end it "finds, then switches to a session" do @job_manager.jobs = [] allow(@root_context).to receive(:ensure_session_select_defined) allow(@root_context).to receive(:jobs).and_return(@job_manager) expect(@job_manager).to receive(:select_shell_session).and_return(:the_shell_session) expect(@job_manager).to receive(:switch).with(:the_shell_session) @root_context.find_or_create_session_for(:foo) end it "creates a new session if an existing one isn't found" do @job_manager.jobs = [] allow(@root_context).to receive(:jobs).and_return(@job_manager) allow(@job_manager).to receive(:select_shell_session).and_return(nil) expect(@root_context).to receive(:irb).with(:foo) @root_context.find_or_create_session_for(:foo) end it "switches to recipe context" do expect(@root_context).to respond_to(:recipe_mode) @shell_client.recipe = :monkeyTime expect(@root_context).to receive(:find_or_create_session_for).with(:monkeyTime) @root_context.recipe_mode end it "switches to attribute context" do expect(@root_context).to respond_to(:attributes_mode) @shell_client.node = "monkeyNodeTime" expect(@root_context).to receive(:find_or_create_session_for).with("monkeyNodeTime") @root_context.attributes_mode end it "has a help command" do expect(@root_context).to respond_to(:help) end it "turns irb tracing on and off" do expect(@root_context).to respond_to(:trace) expect(@root_context.conf).to receive(:use_tracer=).with(true) allow(@root_context).to receive(:tracing?) @root_context.tracing :on end it "says if tracing is on or off" do allow(@root_context.conf).to receive(:use_tracer).and_return(true) expect(@root_context).to receive(:puts).with("tracing is on") @root_context.tracing? end it "prints node attributes" do node = double("node", :attribute => {:foo => :bar}) @shell_client.node = node expect(@root_context).to receive(:pp).with({:foo => :bar}) @root_context.ohai expect(@root_context).to receive(:pp).with(:bar) @root_context.ohai(:foo) end it "resets the recipe and reloads ohai data" do expect(@shell_client).to receive(:reset!) @root_context.reset end it "turns irb echo on and off" do expect(@root_context.conf).to receive(:echo=).with(true) @root_context.echo :on end it "says if echo is on or off" do allow(@root_context.conf).to receive(:echo).and_return(true) expect(@root_context).to receive(:puts).with("echo is on") @root_context.echo? end it "gives access to the stepable iterator" do allow(Shell::StandAloneSession.instance).to receive(:reset!) allow(Shell.session).to receive(:rebuild_context) events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(Chef::Node.new, {}, events) run_context.resource_collection.instance_variable_get(:@resource_list).instance_variable_set(:@iterator, :the_iterator) Shell.session.run_context = run_context expect(@root_context.chef_run).to eq(:the_iterator) end it "lists directory contents" do entries = %w{. .. someFile} expect(Dir).to receive(:entries).with("/tmp").and_return(entries) @root_context.ls "/tmp" end end describe "extending the recipe object" do before do @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(Chef::Node.new, {}, @events) @recipe_object = Chef::Recipe.new(nil, nil, @run_context) Shell::Extensions.extend_context_recipe(@recipe_object) end it "gives a list of the resources" do resource = @recipe_object.file("foo") expect(@recipe_object).to receive(:pp).with(["file[foo]"]) @recipe_object.resources end end end chef-12.3.0/spec/unit/shell/model_wrapper_spec.rb0000644000004100000410000000632012520074675021762 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'ostruct' describe Shell::ModelWrapper do before do @model = OpenStruct.new(:name=>"Chef::Node") @wrapper = Shell::ModelWrapper.new(@model) end describe "when created with an explicit model_symbol" do before do @model = OpenStruct.new(:name=>"Chef::ApiClient") @wrapper = Shell::ModelWrapper.new(@model, :client) end it "uses the explicit model symbol" do expect(@wrapper.model_symbol).to eq(:client) end end it "determines the model symbol from the class name" do expect(@wrapper.model_symbol).to eq(:node) end describe "when listing objects" do before do @node_1 = Chef::Node.new @node_1.name("sammich") @node_2 = Chef::Node.new @node_2.name("yummy") @server_response = {:node_1 => @node_1, :node_2 => @node_2} @wrapper = Shell::ModelWrapper.new(Chef::Node) allow(Chef::Node).to receive(:list).and_return(@server_response) end it "lists fully inflated objects without the resource IDs" do expect(@wrapper.all.size).to eq(2) expect(@wrapper.all).to include(@node_1, @node_2) end it "maps the listed nodes when given a block" do expect(@wrapper.all {|n| n.name }.sort.reverse).to eq(%w{yummy sammich}) end end describe "when searching for objects" do before do @node_1 = Chef::Node.new @node_1.name("sammich") @node_2 = Chef::Node.new @node_2.name("yummy") @server_response = {:node_1 => @node_1, :node_2 => @node_2} @wrapper = Shell::ModelWrapper.new(Chef::Node) # Creating a Chef::Search::Query object tries to read the private key... @searcher = double("Chef::Search::Query #{__FILE__}:#{__LINE__}") allow(Chef::Search::Query).to receive(:new).and_return(@searcher) end it "falls back to listing the objects when the 'query' is :all" do allow(Chef::Node).to receive(:list).and_return(@server_response) expect(@wrapper.find(:all)).to include(@node_1, @node_2) end it "searches for objects using the given query string" do expect(@searcher).to receive(:search).with(:node, 'name:app*').and_yield(@node_1).and_yield(@node_2) expect(@wrapper.find("name:app*")).to include(@node_1, @node_2) end it "creates a 'AND'-joined query string from a HASH" do # Hash order woes expect(@searcher).to receive(:search).with(:node, 'name:app* AND name:app*').and_yield(@node_1).and_yield(@node_2) expect(@wrapper.find(:name=>"app*",'name'=>"app*")).to include(@node_1, @node_2) end end end chef-12.3.0/spec/unit/shell/shell_session_spec.rb0000644000004100000410000001527712520074675022007 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 "ostruct" class TestableShellSession < Shell::ShellSession def rebuild_node nil end def rebuild_collection nil end def loading nil end def loading_complete nil end end describe Shell::ShellSession do it "is a singleton object" do expect(Shell::ShellSession).to include(Singleton) end end describe Shell::ClientSession do before do Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new('shell::override')] } @chef_rest = double("Chef::REST") @session = Shell::ClientSession.instance @node = Chef::Node.build("foo") @session.node = @node @client = double("Chef::Client.new", :run_ohai => true, :load_node => true, :build_node => true, :register => true, :sync_cookbooks => {}) end it "builds the node's run_context with the proper environment" do @session.instance_variable_set(:@client, @client) @expansion = Chef::RunList::RunListExpansion.new(@node.chef_environment, []) expect(@node.run_list).to receive(:expand).with(@node.chef_environment).and_return(@expansion) expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(@chef_rest) @session.rebuild_context end it "passes the shell CLI args to the client" do expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client) @session.send(:rebuild_node) end end describe Shell::StandAloneSession do before do Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new('shell::override')] } @session = Shell::StandAloneSession.instance @node = @session.node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = @session.run_context = Chef::RunContext.new(@node, {}, @events) @recipe = @session.recipe = Chef::Recipe.new(nil, nil, @run_context) Shell::Extensions.extend_context_recipe(@recipe) end it "has a run_context" do expect(@session.run_context).to equal(@run_context) end it "returns a collection based on it's standalone recipe file" do expect(@session.resource_collection).to eq(@recipe.run_context.resource_collection) end it "gives nil for the definitions (for now)" do expect(@session.definitions).to be_nil end it "gives nil for the cookbook_loader" do expect(@session.cookbook_loader).to be_nil end it "runs chef with the standalone recipe" do allow(@session).to receive(:node_built?).and_return(true) allow(Chef::Log).to receive(:level) chef_runner = double("Chef::Runner.new", :converge => :converged) # pre-heat resource collection cache @session.resource_collection expect(Chef::Runner).to receive(:new).with(@session.recipe.run_context).and_return(chef_runner) expect(@recipe.run_chef).to eq(:converged) end it "passes the shell CLI args to the client" do @client = double("Chef::Client.new", :run_ohai => true, :load_node => true, :build_node => true, :register => true, :sync_cookbooks => {}) expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client) @session.send(:rebuild_node) end end describe Shell::SoloSession do before do Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new('shell::override')] } Chef::Config[:shell_solo] = true @session = Shell::SoloSession.instance @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = @session.run_context = Chef::RunContext.new(@node, {}, @events) @session.node = @node @recipe = @session.recipe = Chef::Recipe.new(nil, nil, @run_context) Shell::Extensions.extend_context_recipe(@recipe) end after do Chef::Config[:shell_solo] = nil end it "returns a collection based on it's compilation object and the extra recipe provided by chef-shell" do allow(@session).to receive(:node_built?).and_return(true) kitteh = Chef::Resource::Cat.new("keyboard") @recipe.run_context.resource_collection << kitteh expect(@session.resource_collection).to include(kitteh) end it "returns definitions from its compilation object" do expect(@session.definitions).to eq(@run_context.definitions) end it "keeps json attribs and passes them to the node for consumption" do @session.node_attributes = {"besnard_lakes" => "are_the_dark_horse"} expect(@session.node.besnard_lakes).to eq("are_the_dark_horse") #pending "1) keep attribs in an ivar 2) pass them to the node 3) feed them to the node on reset" end it "generates its resource collection from the compiled cookbooks and the ad hoc recipe" do allow(@session).to receive(:node_built?).and_return(true) kitteh_cat = Chef::Resource::Cat.new("kitteh") @run_context.resource_collection << kitteh_cat keyboard_cat = Chef::Resource::Cat.new("keyboard_cat") @recipe.run_context.resource_collection << keyboard_cat #@session.rebuild_collection expect(@session.resource_collection).to include(kitteh_cat, keyboard_cat) end it "runs chef with a resource collection from the compiled cookbooks" do allow(@session).to receive(:node_built?).and_return(true) allow(Chef::Log).to receive(:level) chef_runner = double("Chef::Runner.new", :converge => :converged) expect(Chef::Runner).to receive(:new).with(an_instance_of(Chef::RunContext)).and_return(chef_runner) expect(@recipe.run_chef).to eq(:converged) end it "passes the shell CLI args to the client" do @client = double("Chef::Client.new", :run_ohai => true, :load_node => true, :build_node => true, :register => true, :sync_cookbooks => {}) expect(Chef::Client).to receive(:new).with(nil, Chef::Config[:shell_config]).and_return(@client) @session.send(:rebuild_node) end end chef-12.3.0/spec/unit/config_fetcher_spec.rb0000644000004100000410000000513112520074675020757 0ustar www-datawww-datarequire 'spec_helper' require 'chef/config_fetcher' describe Chef::ConfigFetcher do let(:valid_json) { Chef::JSONCompat.to_json({:a=>"b"}) } let(:invalid_json) { %q[{"syntax-error": "missing quote}] } let(:http) { double("Chef::HTTP::Simple") } let(:config_location_regex) { Regexp.escape(config_location) } let(:invalid_json_error_regex) { %r[Could not parse the provided JSON file \(#{config_location_regex}\)] } let(:fetcher) { Chef::ConfigFetcher.new(config_location) } context "when loading a local file" do let(:config_location) { "/etc/chef/client.rb" } let(:config_content) { "# The client.rb content" } it "reads the file from disk" do expect(::File).to receive(:read). with(config_location). and_return(config_content) expect(fetcher.read_config).to eq(config_content) end context "and consuming JSON" do let(:config_location) { "/etc/chef/first-boot.json" } it "returns the parsed JSON" do expect(::File).to receive(:read). with(config_location). and_return(valid_json) expect(fetcher.fetch_json).to eq({"a" => "b"}) end context "and the JSON is invalid" do it "reports the JSON error" do expect(::File).to receive(:read). with(config_location). and_return(invalid_json) expect(Chef::Application).to receive(:fatal!). with(invalid_json_error_regex, 2) fetcher.fetch_json end end end end context "when loading a file over HTTP" do let(:config_location) { "https://example.com/client.rb" } let(:config_content) { "# The client.rb content" } before do expect(Chef::HTTP::Simple).to receive(:new). with(config_location). and_return(http) end it "reads the file over HTTP" do expect(http).to receive(:get). with("").and_return(config_content) expect(fetcher.read_config).to eq(config_content) end context "and consuming JSON" do let(:config_location) { "https://example.com/foo.json" } it "fetches the file and parses it" do expect(http).to receive(:get). with("").and_return(valid_json) expect(fetcher.fetch_json).to eq({"a" => "b"}) end context "and the JSON is invalid" do it "reports the JSON error" do expect(http).to receive(:get). with("").and_return(invalid_json) expect(Chef::Application).to receive(:fatal!). with(invalid_json_error_regex, 2) fetcher.fetch_json end end end end end chef-12.3.0/spec/unit/http/0000755000004100000410000000000012520074675015432 5ustar www-datawww-datachef-12.3.0/spec/unit/http/validate_content_length_spec.rb0000644000004100000410000001262312520074675023661 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'stringio' describe Chef::HTTP::ValidateContentLength do class TestClient < Chef::HTTP use Chef::HTTP::ValidateContentLength end let(:method) { "GET" } let(:url) { "http://dummy.com" } let(:headers) { {} } let(:data) { false } let(:request) { } let(:return_value) { "200" } # Test Variables let(:request_type) { :streaming } let(:content_length_value) { 23 } let(:streaming_length) { 23 } let(:response_body) { "Thanks for checking in." } let(:response_headers) { { "content-length" => content_length_value } } let(:response) { m = double('HttpResponse', :body => response_body) allow(m).to receive(:[]) do |key| response_headers[key] end m } let(:middleware) { client = TestClient.new(url) client.middlewares[0] } def run_content_length_validation stream_handler = middleware.stream_response_handler(response) middleware.handle_request(method, url, headers, data) case request_type when :streaming # First stream the data data_length = streaming_length while data_length > 0 chunk_size = data_length > 10 ? 10 : data_length stream_handler.handle_chunk(double("Chunk", :bytesize => chunk_size)) data_length -= chunk_size end # Finally call stream complete middleware.handle_stream_complete(response, request, return_value) when :direct middleware.handle_response(response, request, return_value) else raise "Unknown request_type: #{request_type}" end end let(:debug_stream) { StringIO.new } let(:debug_output) { debug_stream.string } before(:each) do @original_log_level = Chef::Log.level Chef::Log.level = :debug allow(Chef::Log).to receive(:debug) do |message| debug_stream.puts message end end after(:each) do Chef::Log.level = @original_log_level end describe "without response body" do let(:request_type) { :direct } let(:response_body) { "Thanks for checking in." } it "shouldn't raise error" do expect { run_content_length_validation }.not_to raise_error end end describe "without Content-Length header" do let(:response_headers) { { } } [ "direct", "streaming" ].each do |req_type| describe "when running #{req_type} request" do let(:request_type) { req_type.to_sym } it "should skip validation and log for debug" do run_content_length_validation expect(debug_output).to include("HTTP server did not include a Content-Length header in response") end end end end describe "with correct Content-Length header" do [ "direct", "streaming" ].each do |req_type| describe "when running #{req_type} request" do let(:request_type) { req_type.to_sym } it "should validate correctly" do run_content_length_validation expect(debug_output).to include("Content-Length validated correctly.") end end end end describe "with wrong Content-Length header" do let(:content_length_value) { 25 } [ "direct", "streaming" ].each do |req_type| describe "when running #{req_type} request" do let(:request_type) { req_type.to_sym } it "should raise ContentLengthMismatch error" do expect { run_content_length_validation }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end end end describe "when download is interrupted" do let(:streaming_length) { 12 } it "should raise ContentLengthMismatch error" do expect { run_content_length_validation }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end describe "when Transfer-Encoding & Content-Length is set" do let(:response_headers) { { "content-length" => content_length_value, "transfer-encoding" => "chunked" } } [ "direct", "streaming" ].each do |req_type| describe "when running #{req_type} request" do let(:request_type) { req_type.to_sym } it "should skip validation and log for debug" do run_content_length_validation expect(debug_output).to include("Transfer-Encoding header is set, skipping Content-Length check.") end end end end describe "when client is being reused" do before do run_content_length_validation expect(debug_output).to include("Content-Length validated correctly.") end it "should reset internal counter" do expect(middleware.instance_variable_get(:@content_length_counter)).to be_nil end it "should validate correctly second time" do run_content_length_validation expect(debug_output).to include("Content-Length validated correctly.") end end end chef-12.3.0/spec/unit/http/basic_client_spec.rb0000644000004100000410000000572412520074675021420 0ustar www-datawww-data# # Author:: Cameron Cope () # 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/http/basic_client' describe "HTTP Connection" do let(:uri) { URI("https://example.com:4443") } subject(:basic_client) { Chef::HTTP::BasicClient.new(uri) } describe ".new" do it "creates an instance" do subject end end describe "#build_http_client" do it "should build an http client" do subject.build_http_client end it "should set an open timeout" do expect(subject.build_http_client.open_timeout).not_to be_nil end end describe "#proxy_uri" do shared_examples_for "a proxy uri" do let(:proxy_host) { "proxy.mycorp.com" } let(:proxy_port) { 8080 } let(:proxy) { "#{proxy_prefix}#{proxy_host}:#{proxy_port}" } it "should contain the host" do proxy_uri = subject.proxy_uri expect(proxy_uri.host).to eq(proxy_host) end it "should contain the port" do proxy_uri = subject.proxy_uri expect(proxy_uri.port).to eq(proxy_port) end end context "when the config setting is normalized (does not contain the scheme)" do include_examples "a proxy uri" do let(:proxy_prefix) { "" } before do Chef::Config["#{uri.scheme}_proxy"] = proxy Chef::Config[:no_proxy] = nil end end end context "when the config setting is not normalized (contains the scheme)" do include_examples "a proxy uri" do let(:proxy_prefix) { "#{uri.scheme}://" } before do Chef::Config["#{uri.scheme}_proxy"] = proxy Chef::Config[:no_proxy] = nil end end end context "when the proxy is set by the environment" do include_examples "a proxy uri" do let(:env) do { "https_proxy" => "https://proxy.mycorp.com:8080", "https_proxy_user" => "jane_username", "https_proxy_pass" => "opensesame" } end let(:proxy_uri) { URI.parse(env["https_proxy"]) } before do allow(basic_client).to receive(:env).and_return(env) end it "sets the proxy user" do expect(basic_client.http_proxy_user(proxy_uri)).to eq("jane_username") end it "sets the proxy pass" do expect(basic_client.http_proxy_pass(proxy_uri)).to eq("opensesame") end end end end end chef-12.3.0/spec/unit/http/ssl_policies_spec.rb0000644000004100000410000001513612520074675021467 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010, 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/http/ssl_policies' describe "HTTP SSL Policy" do before do Chef::Config[:ssl_client_cert] = nil Chef::Config[:ssl_client_key] = nil Chef::Config[:ssl_ca_path] = nil Chef::Config[:ssl_ca_file] = nil end let(:unconfigured_http_client) { Net::HTTP.new("example.com", 443) } let(:http_client) do unconfigured_http_client.use_ssl = true ssl_policy.apply unconfigured_http_client end describe Chef::HTTP::DefaultSSLPolicy do let(:ssl_policy) { Chef::HTTP::DefaultSSLPolicy.new(unconfigured_http_client) } describe "when configured with :ssl_verify_mode set to :verify peer" do before do Chef::Config[:ssl_verify_mode] = :verify_peer end it "configures the HTTP client to use SSL when given a URL with the https protocol" do expect(http_client.use_ssl?).to be_truthy end it "sets the OpenSSL verify mode to verify_peer" do expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) end it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here" expect {http_client}.to raise_error(Chef::Exceptions::ConfigurationError) end it "should set the CA path if that is set in the configuration" do Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl") expect(http_client.ca_path).to eq(File.join(CHEF_SPEC_DATA, "ssl")) end it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here" expect {http_client}.to raise_error(Chef::Exceptions::ConfigurationError) end it "should set the CA file if that is set in the configuration" do Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + '/ssl/5e707473.0' expect(http_client.ca_file).to eq(CHEF_SPEC_DATA + '/ssl/5e707473.0') end end describe "when configured with :ssl_verify_mode set to :verify peer" do before do @url = URI.parse("https://chef.example.com:4443/") Chef::Config[:ssl_verify_mode] = :verify_none end it "sets the OpenSSL verify mode to :verify_none" do expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end end describe "when configured with a client certificate" do before {@url = URI.parse("https://chef.example.com:4443/")} it "raises ConfigurationError if the certificate file doesn't exist" do Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here" Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + '/ssl/chef-rspec.key' expect {http_client}.to raise_error(Chef::Exceptions::ConfigurationError) end it "raises ConfigurationError if the certificate file doesn't exist" do Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + '/ssl/chef-rspec.cert' Chef::Config[:ssl_client_key] = "/dev/null/nothing_here" expect {http_client}.to raise_error(Chef::Exceptions::ConfigurationError) end it "raises a ConfigurationError if one of :ssl_client_cert and :ssl_client_key is set but not both" do Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here" Chef::Config[:ssl_client_key] = nil expect {http_client}.to raise_error(Chef::Exceptions::ConfigurationError) end it "configures the HTTP client's cert and private key" do Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + '/ssl/chef-rspec.cert' Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + '/ssl/chef-rspec.key' expect(http_client.cert.to_s).to eq(OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + '/ssl/chef-rspec.cert')).to_s) expect(http_client.key.to_s).to eq(IO.read(CHEF_SPEC_DATA + '/ssl/chef-rspec.key')) end end context "when additional certs are located in the trusted_certs dir" do let(:self_signed_crt_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "example.crt") } let(:self_signed_crt) { OpenSSL::X509::Certificate.new(File.read(self_signed_crt_path)) } let(:additional_pem_path) { File.join(CHEF_SPEC_DATA, "trusted_certs", "opscode.pem") } let(:additional_pem) { OpenSSL::X509::Certificate.new(File.read(additional_pem_path)) } before do Chef::Config.trusted_certs_dir = File.join(CHEF_SPEC_DATA, "trusted_certs") end it "enables verification of self-signed certificates" do expect(http_client.cert_store.verify(self_signed_crt)).to be_truthy end it "enables verification of cert chains" do # This cert is signed by DigiCert so it would be valid in normal SSL usage. # The chain goes: # trusted root -> intermediate -> opscode.pem # In this test, the intermediate has to be loaded and trusted in order # for verification to work correctly. # If the machine running the test doesn't have ruby SSL configured correctly, # then the root cert also has to be loaded for the test to succeed. # The system under test **SHOULD** do both of these things. expect(http_client.cert_store.verify(additional_pem)).to be_truthy end context "and some certs are duplicates" do it "skips duplicate certs" do # For whatever reason, OpenSSL errors out when adding a # cert you already have to the certificate store. ssl_policy.set_custom_certs ssl_policy.set_custom_certs #should not raise an error end end end end describe Chef::HTTP::APISSLPolicy do let(:ssl_policy) { Chef::HTTP::APISSLPolicy.new(unconfigured_http_client) } context "when verify_api_cert is set" do before do Chef::Config[:verify_api_cert] = true end it "sets the OpenSSL verify mode to verify_peer" do expect(http_client.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) end end end end chef-12.3.0/spec/unit/http/socketless_chef_zero_client_spec.rb0000644000004100000410000001263512520074675024541 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/http/socketless_chef_zero_client' describe Chef::HTTP::SocketlessChefZeroClient do let(:relative_url) { "" } let(:uri_str) { "chefzero://localhost:1/#{relative_url}" } let(:uri) { URI(uri_str) } subject(:zero_client) { Chef::HTTP::SocketlessChefZeroClient.new(uri) } it "has a host" do expect(zero_client.host).to eq("localhost") end it "has a port" do expect(zero_client.port).to eq(1) end describe "converting requests to rack format" do let(:expected_rack_req) do { "SCRIPT_NAME" => "", "SERVER_NAME" => "localhost", "REQUEST_METHOD" => method.to_s.upcase, "PATH_INFO" => uri.path, "QUERY_STRING" => uri.query, "SERVER_PORT" => uri.port, "HTTP_HOST" => "localhost:#{uri.port}", "rack.url_scheme" => "chefzero", } end context "when the request has no body" do let(:method) { :GET } let(:relative_url) { "clients" } let(:headers) { { "Accept" => "application/json" } } let(:body) { false } let(:expected_body_str) { "" } let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) } it "creates a rack request env" do # StringIO doesn't implement == in a way that we can compare, so we # check rack.input individually and then iterate over everything else expect(rack_req["rack.input"].string).to eq(expected_body_str) expected_rack_req.each do |key, value| expect(rack_req[key]).to eq(value) end end end context "when the request has a body" do let(:method) { :PUT } let(:relative_url) { "clients/foo" } let(:headers) { { "Accept" => "application/json" } } let(:body) { "bunch o' JSON" } let(:expected_body_str) { "bunch o' JSON" } let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) } it "creates a rack request env" do # StringIO doesn't implement == in a way that we can compare, so we # check rack.input individually and then iterate over everything else expect(rack_req["rack.input"].string).to eq(expected_body_str) expected_rack_req.each do |key, value| expect(rack_req[key]).to eq(value) end end end end describe "converting responses to Net::HTTP objects" do let(:net_http_response) { zero_client.to_net_http(code, headers, body) } context "when the request was successful (2XX)" do let(:code) { 200 } let(:headers) { { "Content-Type" => "Application/JSON" } } let(:body) { [ "bunch o' JSON" ] } it "creates a Net::HTTP success response object" do expect(net_http_response).to be_a_kind_of(Net::HTTPOK) expect(net_http_response.read_body).to eq("bunch o' JSON") expect(net_http_response["content-type"]).to eq("Application/JSON") end it "does not fail when calling read_body with a block" do expect(net_http_response.read_body {|chunk| chunk }).to eq("bunch o' JSON") end end context "when the requested object doesn't exist (404)" do let(:code) { 404 } let(:headers) { { "Content-Type" => "Application/JSON" } } let(:body) { [ "nope" ] } it "creates a Net::HTTPNotFound response object" do expect(net_http_response).to be_a_kind_of(Net::HTTPNotFound) end end end describe "request-response round trip" do let(:method) { :GET } let(:relative_url) { "clients" } let(:headers) { { "Accept" => "application/json" } } let(:body) { false } let(:expected_rack_req) do { "SCRIPT_NAME" => "", "SERVER_NAME" => "localhost", "REQUEST_METHOD" => method.to_s.upcase, "PATH_INFO" => uri.path, "QUERY_STRING" => uri.query, "SERVER_PORT" => uri.port, "HTTP_HOST" => "localhost:#{uri.port}", "rack.url_scheme" => "chefzero", "rack.input" => an_instance_of(StringIO), } end let(:response_code) { 200 } let(:response_headers) { { "Content-Type" => "Application/JSON" } } let(:response_body) { [ "bunch o' JSON" ] } let(:rack_response) { [ response_code, response_headers, response_body ] } let(:response) { zero_client.request(method, uri, body, headers) } before do expect(ChefZero::SocketlessServerMap).to receive(:request).with(1, expected_rack_req).and_return(rack_response) end it "makes a rack request to Chef Zero and returns the response as a Net::HTTP object" do _client, net_http_response = response expect(net_http_response).to be_a_kind_of(Net::HTTPOK) expect(net_http_response.code).to eq("200") expect(net_http_response.body).to eq("bunch o' JSON") end end end chef-12.3.0/spec/unit/http/json_input_spec.rb0000644000004100000410000000775512520074675021177 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/http/json_input' describe Chef::HTTP::JSONInput do let(:json_encoder) { described_class.new() } let(:url) { URI.parse("http://example.com") } let(:headers) { {} } def handle_request json_encoder.handle_request(http_method, url, headers, data) end it "passes the response unmodified" do http_response = double("Net::HTTPSuccess") request = double("Chef::HTTP::HTTPRequest") return_value = "response body" result = json_encoder.handle_response(http_response, request, return_value) expect(result).to eq([http_response, request, return_value]) end it "doesn't handle streaming responses" do http_response = double("Net::HTTPSuccess") expect(json_encoder.stream_response_handler(http_response)).to be nil end it "does nothing for stream completion" do http_response = double("Net::HTTPSuccess") request = double("Chef::HTTP::HTTPRequest") return_value = "response body" result = json_encoder.handle_response(http_response, request, return_value) expect(result).to eq([http_response, request, return_value]) end context "when handling a request with no body" do let(:http_method) { :get } let(:data) { nil } it "passes the request unmodified" do expect(handle_request).to eq([http_method, url, headers, data]) end end context "when the request should be serialized" do let(:http_method) { :put } let(:data) { {foo: "bar"} } let(:expected_data) { %q[{"foo":"bar"}] } context "and the request has a ruby object as the body and no explicit content-type" do it "serializes the body to json" do # Headers Hash get mutated, so we start by asserting it's empty: expect(headers).to be_empty expect(handle_request).to eq([http_method, url, headers, expected_data]) # Now the headers Hash should have json content type: expect(headers).to have_key("Content-Type") expect(headers["Content-Type"]).to eq("application/json") end end context "ant the request has an explicit content type of json" do it "serializes the body to json when content-type is all lowercase" do headers["content-type"] = "application/json" expect(handle_request).to eq([http_method, url, headers, expected_data]) # Content-Type header should be normalized: expect(headers.size).to eq(1) expect(headers).to have_key("Content-Type") expect(headers["Content-Type"]).to eq("application/json") end end end context "when handling a request with an explicit content type other than json" do let(:http_method) { :put } let(:data) { "some arbitrary bytes" } it "does not serialize the body to json when content type is given as lowercase" do headers["content-type"] = "application/x-binary" expect(handle_request).to eq([http_method, url, headers, data]) # not normalized expect(headers).to eq({"content-type" => "application/x-binary"}) end it "does not serialize the body to json when content type is given in capitalized form" do headers["Content-Type"] = "application/x-binary" expect(handle_request).to eq([http_method, url, headers, data]) # not normalized expect(headers).to eq({"Content-Type" => "application/x-binary"}) end end end chef-12.3.0/spec/unit/http/simple_spec.rb0000644000004100000410000000236212520074675020265 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::HTTP::Simple do it "should have content length validation middleware after compressor middleware" do client = Chef::HTTP::Simple.new("dummy.com") middlewares = client.instance_variable_get(:@middlewares) content_length = middlewares.find_index { |e| e.is_a? Chef::HTTP::ValidateContentLength } decompressor = middlewares.find_index { |e| e.is_a? Chef::HTTP::Decompressor } expect(content_length).not_to be_nil expect(decompressor).not_to be_nil expect(decompressor < content_length).to be_truthy end end chef-12.3.0/spec/unit/http/http_request_spec.rb0000644000004100000410000000574212520074675021530 0ustar www-datawww-data# # Author:: Klaas Jan Wierenga () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::HTTP::HTTPRequest do context "with HTTP url scheme" do it "should not include port 80 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com'), '') expect(request.headers['Host']).to eql('dummy.com') end it "should not include explicit port 80 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com:80'), '') expect(request.headers['Host']).to eql('dummy.com') end it "should include explicit port 8000 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com:8000'), '') expect(request.headers['Host']).to eql('dummy.com:8000') end it "should include explicit 443 port in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com:443'), '') expect(request.headers['Host']).to eql('dummy.com:443') end it "should pass on explicit Host header unchanged" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com:8000'), '', { 'Host' => 'yourhost.com:8888' }) expect(request.headers['Host']).to eql('yourhost.com:8888') end end context "with HTTPS url scheme" do it "should not include port 443 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('https://dummy.com'), '') expect(request.headers['Host']).to eql('dummy.com') end it "should include explicit port 80 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('https://dummy.com:80'), '') expect(request.headers['Host']).to eql('dummy.com:80') end it "should include explicit port 8000 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('https://dummy.com:8000'), '') expect(request.headers['Host']).to eql('dummy.com:8000') end it "should not include explicit port 443 in Host header" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('https://dummy.com:443'), '') expect(request.headers['Host']).to eql('dummy.com') end end it "should pass on explicit Host header unchanged" do request = Chef::HTTP::HTTPRequest.new(:GET, URI('http://dummy.com:8000'), '', { 'Host' => 'myhost.com:80' }) expect(request.headers['Host']).to eql('myhost.com:80') end end chef-12.3.0/spec/unit/handler/0000755000004100000410000000000012520074675016070 5ustar www-datawww-datachef-12.3.0/spec/unit/handler/json_file_spec.rb0000644000004100000410000000445612520074675021410 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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::Handler::JsonFile do before(:each) do @handler = Chef::Handler::JsonFile.new(:the_sun => "will rise", :path => '/tmp/foobarbazqux') end it "accepts arbitrary config options" do expect(@handler.config[:the_sun]).to eq("will rise") end it "creates the directory where the reports will be saved" do expect(FileUtils).to receive(:mkdir_p).with('/tmp/foobarbazqux') expect(File).to receive(:chmod).with(00700, '/tmp/foobarbazqux') @handler.build_report_dir end describe "when reporting success" do before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_status = Chef::RunStatus.new(@node, @events) @expected_time = Time.now allow(Time).to receive(:now).and_return(@expected_time, @expected_time + 5) @run_status.start_clock @run_status.stop_clock @run_context = Chef::RunContext.new(@node, {}, @events) @run_status.run_context = @run_context @run_status.exception = Exception.new("Boy howdy!") @file_mock = StringIO.new allow(File).to receive(:open).and_yield(@file_mock) end it "saves run status data to a file as JSON" do expect(@handler).to receive(:build_report_dir) @handler.run_report_unsafe(@run_status) reported_data = Chef::JSONCompat.from_json(@file_mock.string) expect(reported_data['exception']).to eq("Exception: Boy howdy!") expect(reported_data['start_time']).to eq(@expected_time.to_s) expect(reported_data['end_time']).to eq((@expected_time + 5).to_s) expect(reported_data['elapsed_time']).to eq(5) end end end chef-12.3.0/spec/unit/version_class_spec.rb0000644000004100000410000001305012520074675020663 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright (c) 2010 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/version_class' describe Chef::Version do before do @v0 = Chef::Version.new "0.0.0" @v123 = Chef::Version.new "1.2.3" end it "should turn itself into a string" do expect(@v0.to_s).to eq("0.0.0") expect(@v123.to_s).to eq("1.2.3") end it "should make a round trip with its string representation" do a = Chef::Version.new(@v123.to_s) expect(a).to eq(@v123) end it "should transform 1.2 to 1.2.0" do expect(Chef::Version.new("1.2").to_s).to eq("1.2.0") end it "should transform 01.002.0003 to 1.2.3" do a = Chef::Version.new "01.002.0003" expect(a).to eq(@v123) end describe "when creating valid Versions" do good_versions = %w(1.2 1.2.3 1000.80.50000 0.300.25 001.02.00003) good_versions.each do |v| it "should accept '#{v}'" do Chef::Version.new v end end end describe "when given bogus input" do bad_versions = ["1.2.3.4", "1.2.a4", "1", "a", "1.2 3", "1.2 a", "1 2 3", "1-2-3", "1_2_3", "1.2_3", "1.2-3"] the_error = Chef::Exceptions::InvalidCookbookVersion bad_versions.each do |v| it "should raise #{the_error} when given '#{v}'" do expect { Chef::Version.new v }.to raise_error(the_error) end end end describe "<=>" do it "should equate versions 1.2 and 1.2.0" do expect(Chef::Version.new("1.2")).to eq(Chef::Version.new("1.2.0")) end it "should equate version 1.04 and 1.4" do expect(Chef::Version.new("1.04")).to eq(Chef::Version.new("1.4")) end it "should treat versions as numbers in the right way" do expect(Chef::Version.new("2.0")).to be < Chef::Version.new("11.0") end it "should sort based on the version number" do examples = [ # smaller, larger ["1.0", "2.0"], ["1.2.3", "1.2.4"], ["1.2.3", "1.3.0"], ["1.2.3", "1.3"], ["1.2.3", "2.1.1"], ["1.2.3", "2.1"], ["1.2", "1.2.4"], ["1.2", "1.3.0"], ["1.2", "1.3"], ["1.2", "2.1.1"], ["1.2", "2.1"] ] examples.each do |smaller, larger| sm = Chef::Version.new(smaller) lg = Chef::Version.new(larger) expect(sm).to be < lg expect(lg).to be > sm expect(sm).not_to eq(lg) end end it "should sort an array of versions" do a = %w{0.0.0 0.0.1 0.1.0 0.1.1 1.0.0 1.1.0 1.1.1}.map do |s| Chef::Version.new(s) end got = a.sort.map {|v| v.to_s } expect(got).to eq(%w{0.0.0 0.0.1 0.1.0 0.1.1 1.0.0 1.1.0 1.1.1}) end it "should sort an array of versions, part 2" do a = %w{9.8.7 1.0.0 1.2.3 4.4.6 4.5.6 0.8.6 4.5.5 5.9.8 3.5.7}.map do |s| Chef::Version.new(s) end got = a.sort.map { |v| v.to_s } expect(got).to eq(%w{0.8.6 1.0.0 1.2.3 3.5.7 4.4.6 4.5.5 4.5.6 5.9.8 9.8.7}) end describe "comparison examples" do [ [ "0.0.0", :>, "0.0.0", false ], [ "0.0.0", :>=, "0.0.0", true ], [ "0.0.0", :==, "0.0.0", true ], [ "0.0.0", :<=, "0.0.0", true ], [ "0.0.0", :<, "0.0.0", false ], [ "0.0.0", :>, "0.0.1", false ], [ "0.0.0", :>=, "0.0.1", false ], [ "0.0.0", :==, "0.0.1", false ], [ "0.0.0", :<=, "0.0.1", true ], [ "0.0.0", :<, "0.0.1", true ], [ "0.0.1", :>, "0.0.1", false ], [ "0.0.1", :>=, "0.0.1", true ], [ "0.0.1", :==, "0.0.1", true ], [ "0.0.1", :<=, "0.0.1", true ], [ "0.0.1", :<, "0.0.1", false ], [ "0.1.0", :>, "0.1.0", false ], [ "0.1.0", :>=, "0.1.0", true ], [ "0.1.0", :==, "0.1.0", true ], [ "0.1.0", :<=, "0.1.0", true ], [ "0.1.0", :<, "0.1.0", false ], [ "0.1.1", :>, "0.1.1", false ], [ "0.1.1", :>=, "0.1.1", true ], [ "0.1.1", :==, "0.1.1", true ], [ "0.1.1", :<=, "0.1.1", true ], [ "0.1.1", :<, "0.1.1", false ], [ "1.0.0", :>, "1.0.0", false ], [ "1.0.0", :>=, "1.0.0", true ], [ "1.0.0", :==, "1.0.0", true ], [ "1.0.0", :<=, "1.0.0", true ], [ "1.0.0", :<, "1.0.0", false ], [ "1.0.0", :>, "0.0.1", true ], [ "1.0.0", :>=, "1.9.2", false ], [ "1.0.0", :==, "9.7.2", false ], [ "1.0.0", :<=, "1.9.1", true ], [ "1.0.0", :<, "1.9.0", true ], [ "1.2.2", :>, "1.2.1", true ], [ "1.2.2", :>=, "1.2.1", true ], [ "1.2.2", :==, "1.2.1", false ], [ "1.2.2", :<=, "1.2.1", false ], [ "1.2.2", :<, "1.2.1", false ] ].each do |spec| it "(#{spec.first(3).join(' ')}) should be #{spec[3]}" do got = Chef::Version.new(spec[0]).send(spec[1], Chef::Version.new(spec[2])) expect(got).to eq(spec[3]) end end end end end chef-12.3.0/spec/unit/cookbook_site_streaming_uploader_spec.rb0000644000004100000410000001704112520074675024613 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/cookbook_site_streaming_uploader' class FakeTempfile def initialize(basename) @basename = basename end def close end def path "#{@basename}.ZZZ" end end describe Chef::CookbookSiteStreamingUploader do describe "create_build_dir" do before(:each) do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, 'cookbooks')) @loader = Chef::CookbookLoader.new(@cookbook_repo) @loader.load_cookbooks allow(File).to receive(:unlink) end it "should create the cookbook tmp dir" do cookbook = @loader[:openldap] files_count = Dir.glob(File.join(@cookbook_repo, cookbook.name.to_s, '**', '*'), File::FNM_DOTMATCH).count { |file| File.file?(file) } expect(Tempfile).to receive(:new).with("chef-#{cookbook.name}-build").and_return(FakeTempfile.new("chef-#{cookbook.name}-build")) expect(FileUtils).to receive(:mkdir_p).exactly(files_count + 1).times expect(FileUtils).to receive(:cp).exactly(files_count).times Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook) end end # create_build_dir describe "make_request" do before(:each) do @uri = "http://cookbooks.dummy.com/api/v1/cookbooks" @secret_filename = File.join(CHEF_SPEC_DATA, 'ssl/private_key.pem') @rsa_key = File.read(@secret_filename) response = Net::HTTPResponse.new('1.0', '200', 'OK') allow_any_instance_of(Net::HTTP).to receive(:request).and_return(response) end it "should send an http request" do expect_any_instance_of(Net::HTTP).to receive(:request) Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) end it "should read the private key file" do expect(File).to receive(:read).with(@secret_filename).and_return(@rsa_key) Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) end it "should add the authentication signed header" do expect_any_instance_of(Mixlib::Authentication::SigningObject).to receive(:sign).and_return({}) Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) end it "should be able to send post requests" do post = Net::HTTP::Post.new(@uri, {}) expect(Net::HTTP::Post).to receive(:new).once.and_return(post) expect(Net::HTTP::Put).not_to receive(:new) expect(Net::HTTP::Get).not_to receive(:new) Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) end it "should be able to send put requests" do put = Net::HTTP::Put.new(@uri, {}) expect(Net::HTTP::Post).not_to receive(:new) expect(Net::HTTP::Put).to receive(:new).once.and_return(put) expect(Net::HTTP::Get).not_to receive(:new) Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, 'bill', @secret_filename) end it "should be able to receive files to attach as argument" do Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, 'bill', @secret_filename, { :myfile => File.new(File.join(CHEF_SPEC_DATA, 'config.rb')), # a dummy file }) end it "should be able to receive strings to attach as argument" do Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, 'bill', @secret_filename, { :mystring => 'Lorem ipsum', }) end it "should be able to receive strings and files as argument at the same time" do Chef::CookbookSiteStreamingUploader.make_request(:put, @uri, 'bill', @secret_filename, { :myfile1 => File.new(File.join(CHEF_SPEC_DATA, 'config.rb')), :mystring1 => 'Lorem ipsum', :myfile2 => File.new(File.join(CHEF_SPEC_DATA, 'config.rb')), :mystring2 => 'Dummy text', }) end describe "http verify mode" do before do @uri = "https://cookbooks.dummy.com/api/v1/cookbooks" uri_info = URI.parse(@uri) @http = Net::HTTP.new(uri_info.host, uri_info.port) expect(Net::HTTP).to receive(:new).with(uri_info.host, uri_info.port).and_return(@http) end it "should be VERIFY_NONE when ssl_verify_mode is :verify_none" do Chef::Config[:ssl_verify_mode] = :verify_none Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) expect(@http.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end it "should be VERIFY_PEER when ssl_verify_mode is :verify_peer" do Chef::Config[:ssl_verify_mode] = :verify_peer Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename) expect(@http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) end end end # make_request describe "StreamPart" do before(:each) do @file = File.new(File.join(CHEF_SPEC_DATA, 'config.rb')) @stream_part = Chef::CookbookSiteStreamingUploader::StreamPart.new(@file, File.size(@file)) end it "should create a StreamPart" do expect(@stream_part).to be_instance_of(Chef::CookbookSiteStreamingUploader::StreamPart) end it "should expose its size" do expect(@stream_part.size).to eql(File.size(@file)) end it "should read with offset and how_much" do content = @file.read(4) @file.rewind expect(@stream_part.read(0, 4)).to eql(content) end end # StreamPart describe "StringPart" do before(:each) do @str = 'What a boring string' @string_part = Chef::CookbookSiteStreamingUploader::StringPart.new(@str) end it "should create a StringPart" do expect(@string_part).to be_instance_of(Chef::CookbookSiteStreamingUploader::StringPart) end it "should expose its size" do expect(@string_part.size).to eql(@str.size) end it "should read with offset and how_much" do expect(@string_part.read(2, 4)).to eql(@str[2, 4]) end end # StringPart describe "MultipartStream" do before(:each) do @string1 = "stream1" @string2 = "stream2" @stream1 = Chef::CookbookSiteStreamingUploader::StringPart.new(@string1) @stream2 = Chef::CookbookSiteStreamingUploader::StringPart.new(@string2) @parts = [ @stream1, @stream2 ] @multipart_stream = Chef::CookbookSiteStreamingUploader::MultipartStream.new(@parts) end it "should create a MultipartStream" do expect(@multipart_stream).to be_instance_of(Chef::CookbookSiteStreamingUploader::MultipartStream) end it "should expose its size" do expect(@multipart_stream.size).to eql(@stream1.size + @stream2.size) end it "should read with how_much" do expect(@multipart_stream.read(10)).to eql("#{@string1}#{@string2}"[0, 10]) end it "should read receiving destination buffer as second argument (CHEF-4456: Ruby 2 compat)" do dst_buf = '' @multipart_stream.read(10, dst_buf) expect(dst_buf).to eql("#{@string1}#{@string2}"[0, 10]) end end # MultipartStream end chef-12.3.0/spec/unit/resource_reporter_spec.rb0000644000004100000410000007272412520074675021577 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Prajakta Purohit () # Author:: Tyler Cloke () # # 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 File.expand_path("../../spec_helper", __FILE__) require 'chef/resource_reporter' require 'socket' describe Chef::ResourceReporter do before(:all) do @reporting_toggle_default = Chef::Config[:enable_reporting] Chef::Config[:enable_reporting] = true end after(:all) do Chef::Config[:enable_reporting] = @reporting_toggle_default end before do @node = Chef::Node.new @node.name("spitfire") @rest_client = double("Chef::REST (mock)") allow(@rest_client).to receive(:post_rest).and_return(true) @resource_reporter = Chef::ResourceReporter.new(@rest_client) @new_resource = Chef::Resource::File.new("/tmp/a-file.txt") @cookbook_name = "monkey" @new_resource.cookbook_name = @cookbook_name @cookbook_version = double("Cookbook::Version", :version => "1.2.3") allow(@new_resource).to receive(:cookbook_version).and_return(@cookbook_version) @current_resource = Chef::Resource::File.new("/tmp/a-file.txt") @start_time = Time.new @end_time = Time.new + 20 @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @run_status = Chef::RunStatus.new(@node, @events) @run_id = @run_status.run_id allow(Time).to receive(:now).and_return(@start_time, @end_time) end context "when first created" do it "has no updated resources" do expect(@resource_reporter.updated_resources.size).to eq(0) end it "reports a successful run" do expect(@resource_reporter.status).to eq("success") end it "assumes the resource history feature is supported" do expect(@resource_reporter.reporting_enabled?).to be_truthy end it "should have no error_descriptions" do expect(@resource_reporter.error_descriptions).to eq({}) # @resource_reporter.error_descriptions.should be_empty # @resource_reporter.should have(0).error_descriptions end end context "after the chef run completes" do before do end it "reports a successful run" do skip "refactor how node gets set." expect(@resource_reporter.status).to eq("success") end end context "when chef fails" do before do allow(@rest_client).to receive(:create_url).and_return("reports/nodes/spitfire/runs/#{@run_id}"); allow(@rest_client).to receive(:raw_http_request).and_return({"result"=>"ok"}); allow(@rest_client).to receive(:post_rest).and_return({"uri"=>"https://example.com/reports/nodes/spitfire/runs/#{@run_id}"}); end context "before converging any resources" do before do @resource_reporter.run_started(@run_status) @exception = Exception.new @resource_reporter.run_failed(@exception) end it "sets the run status to 'failure'" do expect(@resource_reporter.status).to eq("failure") end it "keeps the exception data" do expect(@resource_reporter.exception).to eq(@exception) end end context "when a resource fails before loading current state" do before do @exception = Exception.new @exception.set_backtrace(caller) @resource_reporter.resource_action_start(@new_resource, :create) @resource_reporter.resource_failed(@new_resource, :create, @exception) @resource_reporter.resource_completed(@new_resource) end it "collects the resource as an updated resource" do expect(@resource_reporter.updated_resources.size).to eq(1) end it "collects the desired state of the resource" do update_record = @resource_reporter.updated_resources.first expect(update_record.new_resource).to eq(@new_resource) end end # TODO: make sure a resource that is skipped because of `not_if` doesn't # leave us in a bad state. context "once the a resource's current state is loaded" do before do @resource_reporter.resource_action_start(@new_resource, :create) @resource_reporter.resource_current_state_loaded(@new_resource, :create, @current_resource) end context "and the resource was not updated" do before do @resource_reporter.resource_up_to_date(@new_resource, :create) end it "has no updated resources" do expect(@resource_reporter.updated_resources.size).to eq(0) end end context "and the resource was updated" do before do @new_resource.content("this is the old content") @current_resource.content("this is the new hotness") @resource_reporter.resource_updated(@new_resource, :create) @resource_reporter.resource_completed(@new_resource) end it "collects the updated resource" do expect(@resource_reporter.updated_resources.size).to eq(1) end it "collects the old state of the resource" do update_record = @resource_reporter.updated_resources.first expect(update_record.current_resource).to eq(@current_resource) end it "collects the new state of the resource" do update_record = @resource_reporter.updated_resources.first expect(update_record.new_resource).to eq(@new_resource) end context "and a subsequent resource fails before loading current resource" do before do @next_new_resource = Chef::Resource::Service.new("apache2") @exception = Exception.new @exception.set_backtrace(caller) @resource_reporter.resource_failed(@next_new_resource, :create, @exception) @resource_reporter.resource_completed(@next_new_resource) end it "collects the desired state of the failed resource" do failed_resource_update = @resource_reporter.updated_resources.last expect(failed_resource_update.new_resource).to eq(@next_new_resource) end it "does not have the current state of the failed resource" do failed_resource_update = @resource_reporter.updated_resources.last expect(failed_resource_update.current_resource).to be_nil end end end # Some providers, such as RemoteDirectory and some LWRPs use other # resources for their implementation. These should be hidden from reporting # since we only care about the top-level resource and not the sub-resources # used for implementation. context "and a nested resource is updated" do before do @implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt") @resource_reporter.resource_action_start(@implementation_resource , :create) @resource_reporter.resource_current_state_loaded(@implementation_resource, :create, @implementation_resource) @resource_reporter.resource_updated(@implementation_resource, :create) @resource_reporter.resource_completed(@implementation_resource) @resource_reporter.resource_updated(@new_resource, :create) @resource_reporter.resource_completed(@new_resource) end it "does not collect data about the nested resource" do expect(@resource_reporter.updated_resources.size).to eq(1) end end context "and a nested resource runs but is not updated" do before do @implementation_resource = Chef::Resource::CookbookFile.new("/preseed-file.txt") @resource_reporter.resource_action_start(@implementation_resource , :create) @resource_reporter.resource_current_state_loaded(@implementation_resource, :create, @implementation_resource) @resource_reporter.resource_up_to_date(@implementation_resource, :create) @resource_reporter.resource_completed(@implementation_resource) @resource_reporter.resource_updated(@new_resource, :create) @resource_reporter.resource_completed(@new_resource) end it "does not collect data about the nested resource" do expect(@resource_reporter.updated_resources.size).to eq(1) end end context "and the resource failed to converge" do before do @exception = Exception.new @exception.set_backtrace(caller) @resource_reporter.resource_failed(@new_resource, :create, @exception) @resource_reporter.resource_completed(@new_resource) end it "collects the resource as an updated resource" do expect(@resource_reporter.updated_resources.size).to eq(1) end it "collects the desired state of the resource" do update_record = @resource_reporter.updated_resources.first expect(update_record.new_resource).to eq(@new_resource) end it "collects the current state of the resource" do update_record = @resource_reporter.updated_resources.first expect(update_record.current_resource).to eq(@current_resource) end end end end describe "when generating a report for the server" do before do allow(@rest_client).to receive(:create_url).and_return("reports/nodes/spitfire/runs/#{@run_id}"); allow(@rest_client).to receive(:raw_http_request).and_return({"result"=>"ok"}); allow(@rest_client).to receive(:post_rest).and_return({"uri"=>"https://example.com/reports/nodes/spitfire/runs/#{@run_id}"}); @resource_reporter.run_started(@run_status) end context "when the new_resource does not have a string for name and identity" do context "the new_resource name and id are nil" do before do @bad_resource = Chef::Resource::File.new("/tmp/nameless_file.txt") allow(@bad_resource).to receive(:name).and_return(nil) allow(@bad_resource).to receive(:identity).and_return(nil) @resource_reporter.resource_action_start(@bad_resource, :create) @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource) @resource_reporter.resource_updated(@bad_resource, :create) @resource_reporter.resource_completed(@bad_resource) @run_status.stop_clock @report = @resource_reporter.prepare_run_data @first_update_report = @report["resources"].first end it "resource_name in prepared_run_data is a string" do expect(@first_update_report["name"].class).to eq(String) end it "resource_id in prepared_run_data is a string" do expect(@first_update_report["id"].class).to eq(String) end end context "the new_resource name and id are hashes" do before do @bad_resource = Chef::Resource::File.new("/tmp/filename_as_hash.txt") allow(@bad_resource).to receive(:name).and_return({:foo=>:bar}) allow(@bad_resource).to receive(:identity).and_return({:foo=>:bar}) @resource_reporter.resource_action_start(@bad_resource, :create) @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource) @resource_reporter.resource_updated(@bad_resource, :create) @resource_reporter.resource_completed(@bad_resource) @run_status.stop_clock @report = @resource_reporter.prepare_run_data @first_update_report = @report["resources"].first end # Ruby 1.8.7 flattens out hash to string using join instead of inspect, resulting in # irb(main):001:0> {:foo => :bar}.to_s # => "foobar" # instead of the expected # irb(main):001:0> {:foo => :bar}.to_s # => "{:foo=>:bar}" # Hence checking for the class instead of the actual value. it "resource_name in prepared_run_data is a string" do expect(@first_update_report["name"].class).to eq(String) end it "resource_id in prepared_run_data is a string" do expect(@first_update_report["id"].class).to eq(String) end end end shared_examples_for "a successful client run" do before do # TODO: add inputs to generate expected output. # expected_data = { # "action" : "end", # "resources" : [ # { # "type" : "file", # "id" : "/etc/passwd", # "name" : "User Defined Resource Block Name", # "duration" : "1200", # "result" : "modified", # "before" : { # "state" : "exists", # "group" : "root", # "owner" : "root", # "checksum" : "xyz" # }, # "after" : { # "state" : "modified", # "group" : "root", # "owner" : "root", # "checksum" : "abc" # }, # "delta" : "" # }, # {...} # ], # "status" : "success" # "data" : "" # } @resource_reporter.resource_action_start(new_resource, :create) @resource_reporter.resource_current_state_loaded(new_resource, :create, current_resource) @resource_reporter.resource_updated(new_resource, :create) @resource_reporter.resource_completed(new_resource) @run_status.stop_clock @report = @resource_reporter.prepare_run_data @first_update_report = @report["resources"].first end it "includes the run's status" do expect(@report).to have_key("status") end it "includes a list of updated resources" do expect(@report).to have_key("resources") end it "includes an updated resource's type" do expect(@first_update_report).to have_key("type") end it "includes an updated resource's initial state" do expect(@first_update_report["before"]).to eq(current_resource.state) end it "includes an updated resource's final state" do expect(@first_update_report["after"]).to eq(new_resource.state) end it "includes the resource's name" do expect(@first_update_report["name"]).to eq(new_resource.name) end it "includes the resource's id attribute" do expect(@first_update_report["id"]).to eq(new_resource.identity) end it "includes the elapsed time for the resource to converge" do # TODO: API takes integer number of milliseconds as a string. This # should be an int. expect(@first_update_report).to have_key("duration") expect(@first_update_report["duration"].to_i).to be_within(100).of(0) end it "includes the action executed by the resource" do # TODO: rename as "action" expect(@first_update_report["result"]).to eq("create") end it "includes the cookbook name of the resource" do expect(@first_update_report).to have_key("cookbook_name") expect(@first_update_report["cookbook_name"]).to eq(@cookbook_name) end it "includes the cookbook version of the resource" do expect(@first_update_report).to have_key("cookbook_version") expect(@first_update_report["cookbook_version"]).to eq("1.2.3") end it "includes the total resource count" do expect(@report).to have_key("total_res_count") expect(@report["total_res_count"]).to eq("1") end it "includes the data hash" do expect(@report).to have_key("data") expect(@report["data"]).to eq({}) end it "includes the run_list" do expect(@report).to have_key("run_list") expect(@report["run_list"]).to eq(Chef::JSONCompat.to_json(@run_status.node.run_list)) end it "includes the end_time" do expect(@report).to have_key("end_time") expect(@report["end_time"]).to eq(@run_status.end_time.to_s) end end context "when the resource is a File" do let(:new_resource) { @new_resource } let(:current_resource) { @current_resource } it_should_behave_like "a successful client run" end context "when the resource is a RegistryKey with binary data" do let(:new_resource) do resource = Chef::Resource::RegistryKey.new('Wubba\Lubba\Dub\Dubs') resource.values([ { :name => 'rick', :type => :binary, :data => 255.chr * 1 } ]) allow(resource).to receive(:cookbook_name).and_return(@cookbook_name) allow(resource).to receive(:cookbook_version).and_return(@cookbook_version) resource end let(:current_resource) do resource = Chef::Resource::RegistryKey.new('Wubba\Lubba\Dub\Dubs') resource.values([ { :name => 'rick', :type => :binary, :data => 255.chr * 1 } ]) resource end it_should_behave_like "a successful client run" end context "for an unsuccessful run" do before do @backtrace = ["foo.rb:1 in `foo!'","bar.rb:2 in `bar!","'baz.rb:3 in `baz!'"] @node = Chef::Node.new @node.name("spitfire") @exception = ArgumentError.new allow(@exception).to receive(:inspect).and_return("Net::HTTPServerException") allow(@exception).to receive(:message).and_return("Object not found") allow(@exception).to receive(:backtrace).and_return(@backtrace) @resource_reporter.run_list_expand_failed(@node, @exception) @resource_reporter.run_failed(@exception) @report = @resource_reporter.prepare_run_data end it "includes the exception type in the event data" do expect(@report).to have_key("data") expect(@report["data"]["exception"]).to have_key("class") expect(@report["data"]["exception"]["class"]).to eq("Net::HTTPServerException") end it "includes the exception message in the event data" do expect(@report["data"]["exception"]).to have_key("message") expect(@report["data"]["exception"]["message"]).to eq("Object not found") end it "includes the exception trace in the event data" do expect(@report["data"]["exception"]).to have_key("backtrace") expect(@report["data"]["exception"]["backtrace"]).to eq(Chef::JSONCompat.to_json(@backtrace)) end it "includes the error inspector output in the event data" do expect(@report["data"]["exception"]).to have_key("description") expect(@report["data"]["exception"]["description"]).to include({"title"=>"Error expanding the run_list:", "sections"=>[{"Unexpected Error:" => "ArgumentError: Object not found"}]}) end end context "when new_resource does not have a cookbook_name" do before do @bad_resource = Chef::Resource::File.new("/tmp/a-file.txt") @bad_resource.cookbook_name = nil @resource_reporter.resource_action_start(@bad_resource, :create) @resource_reporter.resource_current_state_loaded(@bad_resource, :create, @current_resource) @resource_reporter.resource_updated(@bad_resource, :create) @resource_reporter.resource_completed(@bad_resource) @run_status.stop_clock @report = @resource_reporter.prepare_run_data @first_update_report = @report["resources"].first end it "includes an updated resource's initial state" do expect(@first_update_report["before"]).to eq(@current_resource.state) end it "includes an updated resource's final state" do expect(@first_update_report["after"]).to eq(@new_resource.state) end it "includes the resource's name" do expect(@first_update_report["name"]).to eq(@new_resource.name) end it "includes the resource's id attribute" do expect(@first_update_report["id"]).to eq(@new_resource.identity) end it "includes the elapsed time for the resource to converge" do # TODO: API takes integer number of milliseconds as a string. This # should be an int. expect(@first_update_report).to have_key("duration") expect(@first_update_report["duration"].to_i).to be_within(100).of(0) end it "includes the action executed by the resource" do # TODO: rename as "action" expect(@first_update_report["result"]).to eq("create") end it "does not include a cookbook name for the resource" do expect(@first_update_report).not_to have_key("cookbook_name") end it "does not include a cookbook version for the resource" do expect(@first_update_report).not_to have_key("cookbook_version") end end context "when including a resource that overrides Resource#state" do before do @current_state_resource = Chef::Resource::WithState.new("Stateful", @run_context) @current_state_resource.state = nil @new_state_resource = Chef::Resource::WithState.new("Stateful", @run_context) @new_state_resource.state = "Running" @resource_reporter.resource_action_start(@new_state_resource, :create) @resource_reporter.resource_current_state_loaded(@new_state_resource, :create, @current_state_resource) @resource_reporter.resource_updated(@new_state_resource, :create) @resource_reporter.resource_completed(@new_state_resource) @run_status.stop_clock @report = @resource_reporter.prepare_run_data @first_update_report = @report["resources"].first end it "sets before to {} instead of nil" do expect(@first_update_report).to have_key("before") expect(@first_update_report['before']).to eq({}) end it "sets after to {} instead of 'Running'" do expect(@first_update_report).to have_key("after") expect(@first_update_report['after']).to eq({}) end end end describe "when updating resource history on the server" do before do @resource_reporter.run_started(@run_status) @run_status.start_clock end context "when the server does not support storing resource history" do before do # 404 getting the run_id @response = Net::HTTPNotFound.new("a response body", "404", "Not Found") @error = Net::HTTPServerException.new("404 message", @response) expect(@rest_client).to receive(:post_rest). with("reports/nodes/spitfire/runs", {:action => :start, :run_id => @run_id, :start_time => @start_time.to_s}, {'X-Ops-Reporting-Protocol-Version' => Chef::ResourceReporter::PROTOCOL_VERSION}). and_raise(@error) end it "assumes the feature is not enabled" do @resource_reporter.run_started(@run_status) expect(@resource_reporter.reporting_enabled?).to be_falsey end it "does not send a resource report to the server" do @resource_reporter.run_started(@run_status) expect(@rest_client).not_to receive(:post_rest) @resource_reporter.run_completed(@node) end it "prints an error about the 404" do expect(Chef::Log).to receive(:debug).with(/404/) @resource_reporter.run_started(@run_status) end end context "when the server returns a 500 to the client" do before do # 500 getting the run_id @response = Net::HTTPInternalServerError.new("a response body", "500", "Internal Server Error") @error = Net::HTTPServerException.new("500 message", @response) expect(@rest_client).to receive(:post_rest). with("reports/nodes/spitfire/runs", {:action => :start, :run_id => @run_id, :start_time => @start_time.to_s}, {'X-Ops-Reporting-Protocol-Version' => Chef::ResourceReporter::PROTOCOL_VERSION}). and_raise(@error) end it "assumes the feature is not enabled" do @resource_reporter.run_started(@run_status) expect(@resource_reporter.reporting_enabled?).to be_falsey end it "does not send a resource report to the server" do @resource_reporter.run_started(@run_status) expect(@rest_client).not_to receive(:post_rest) @resource_reporter.run_completed(@node) end it "prints an error about the error" do expect(Chef::Log).to receive(:info).with(/500/) @resource_reporter.run_started(@run_status) end end context "when the server returns a 500 to the client and enable_reporting_url_fatals is true" do before do @enable_reporting_url_fatals = Chef::Config[:enable_reporting_url_fatals] Chef::Config[:enable_reporting_url_fatals] = true # 500 getting the run_id @response = Net::HTTPInternalServerError.new("a response body", "500", "Internal Server Error") @error = Net::HTTPServerException.new("500 message", @response) expect(@rest_client).to receive(:post_rest). with("reports/nodes/spitfire/runs", {:action => :start, :run_id => @run_id, :start_time => @start_time.to_s}, {'X-Ops-Reporting-Protocol-Version' => Chef::ResourceReporter::PROTOCOL_VERSION}). and_raise(@error) end after do Chef::Config[:enable_reporting_url_fatals] = @enable_reporting_url_fatals end it "fails the run and prints an message about the error" do expect(Chef::Log).to receive(:error).with(/500/) expect { @resource_reporter.run_started(@run_status) }.to raise_error(Net::HTTPServerException) end end context "after creating the run history document" do before do response = {"uri"=>"https://example.com/reports/nodes/spitfire/runs/@run_id"} expect(@rest_client).to receive(:post_rest). with("reports/nodes/spitfire/runs", {:action => :start, :run_id => @run_id, :start_time => @start_time.to_s}, {'X-Ops-Reporting-Protocol-Version' => Chef::ResourceReporter::PROTOCOL_VERSION}). and_return(response) @resource_reporter.run_started(@run_status) end it "creates a run document on the server at the start of the run" do expect(@resource_reporter.run_id).to eq(@run_id) end it "updates the run document with resource updates at the end of the run" do # update some resources... @resource_reporter.resource_action_start(@new_resource, :create) @resource_reporter.resource_current_state_loaded(@new_resource, :create, @current_resource) @resource_reporter.resource_updated(@new_resource, :create) allow(@resource_reporter).to receive(:end_time).and_return(@end_time) @expected_data = @resource_reporter.prepare_run_data post_url = "https://chef_server/example_url" response = {"result"=>"ok"} expect(@rest_client).to receive(:create_url). with("reports/nodes/spitfire/runs/#{@run_id}"). ordered. and_return(post_url) expect(@rest_client).to receive(:raw_http_request).ordered do |method, url, headers, data| expect(method).to eq(:POST) expect(url).to eq(post_url) expect(headers).to eq({'Content-Encoding' => 'gzip', 'X-Ops-Reporting-Protocol-Version' => Chef::ResourceReporter::PROTOCOL_VERSION }) data_stream = Zlib::GzipReader.new(StringIO.new(data)) data = data_stream.read expect(data).to eq(Chef::JSONCompat.to_json(@expected_data)) response end @resource_reporter.run_completed(@node) end end context "when data report post is enabled and the server response fails" do before do @enable_reporting_url_fatals = Chef::Config[:enable_reporting_url_fatals] Chef::Config[:enable_reporting_url_fatals] = true # this call doesn't matter for this context allow(@rest_client).to receive(:create_url) end after do Chef::Config[:enable_reporting_url_fatals] = @enable_reporting_url_fatals end it "should log 4xx errors" do response = Net::HTTPClientError.new("forbidden", "403", "Forbidden") error = Net::HTTPServerException.new("403 message", response) allow(@rest_client).to receive(:raw_http_request).and_raise(error) expect(Chef::Log).to receive(:error).with(/403/) @resource_reporter.post_reporting_data end it "should log error 5xx errors" do response = Net::HTTPServerError.new("internal error", "500", "Internal Server Error") error = Net::HTTPFatalError.new("500 message", response) allow(@rest_client).to receive(:raw_http_request).and_raise(error) expect(Chef::Log).to receive(:error).with(/500/) @resource_reporter.post_reporting_data end it "should log if a socket error happens" do allow(@rest_client).to receive(:raw_http_request).and_raise(SocketError.new("test socket error")) expect(Chef::Log).to receive(:error).with(/test socket error/) @resource_reporter.post_reporting_data end it "should raise if an unkwown error happens" do allow(@rest_client).to receive(:raw_http_request).and_raise(Exception.new) expect { @resource_reporter.post_reporting_data }.to raise_error(Exception) end end end end chef-12.3.0/spec/unit/util/0000755000004100000410000000000012520074675015430 5ustar www-datawww-datachef-12.3.0/spec/unit/util/path_helper_spec.rb0000644000004100000410000002321212520074675021262 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/path_helper' require 'spec_helper' describe Chef::Util::PathHelper do PathHelper = Chef::Util::PathHelper [ false, true ].each do |is_windows| context "on #{is_windows ? "windows" : "unix"}" do before(:each) do allow(Chef::Platform).to receive(:windows?).and_return(is_windows) end describe "join" do it "joins components when some end with separators" do expected = PathHelper.cleanpath("/foo/bar/baz") expected = "C:#{expected}" if is_windows expect(PathHelper.join(is_windows ? 'C:\\foo\\' : "/foo/", "bar", "baz")).to eq(expected) end it "joins components when some end and start with separators" do expected = PathHelper.cleanpath("/foo/bar/baz") expected = "C:#{expected}" if is_windows expect(PathHelper.join(is_windows ? 'C:\\foo\\' : "/foo/", "bar/", "/baz")).to eq(expected) end it "joins components that don't end in separators" do expected = PathHelper.cleanpath("/foo/bar/baz") expected = "C:#{expected}" if is_windows expect(PathHelper.join(is_windows ? 'C:\\foo' : "/foo", "bar", "baz")).to eq(expected) end it "joins starting with '' resolve to absolute paths" do expect(PathHelper.join('', 'a', 'b')).to eq("#{PathHelper.path_separator}a#{PathHelper.path_separator}b") end it "joins ending with '' add a / to the end" do expect(PathHelper.join('a', 'b', '')).to eq("a#{PathHelper.path_separator}b#{PathHelper.path_separator}") end if is_windows it "joins components on Windows when some end with unix separators" do expect(PathHelper.join('C:\\foo/', "bar", "baz")).to eq('C:\\foo\\bar\\baz') end end end if is_windows it "path_separator is \\" do expect(PathHelper.path_separator).to eq('\\') end else it "path_separator is /" do expect(PathHelper.path_separator).to eq('/') end end if is_windows it "cleanpath changes slashes into backslashes and leaves backslashes alone" do expect(PathHelper.cleanpath('/a/b\\c/d/')).to eq('\\a\\b\\c\\d') end it "cleanpath does not remove leading double backslash" do expect(PathHelper.cleanpath('\\\\a/b\\c/d/')).to eq('\\\\a\\b\\c\\d') end else it "cleanpath removes extra slashes alone" do expect(PathHelper.cleanpath('/a///b/c/d/')).to eq('/a/b/c/d') end end describe "dirname" do it "dirname('abc') is '.'" do expect(PathHelper.dirname('abc')).to eq('.') end it "dirname('/') is '/'" do expect(PathHelper.dirname(PathHelper.path_separator)).to eq(PathHelper.path_separator) end it "dirname('a/b/c') is 'a/b'" do expect(PathHelper.dirname(PathHelper.join('a', 'b', 'c'))).to eq(PathHelper.join('a', 'b')) end it "dirname('a/b/c/') is 'a/b'" do expect(PathHelper.dirname(PathHelper.join('a', 'b', 'c', ''))).to eq(PathHelper.join('a', 'b')) end it "dirname('/a/b/c') is '/a/b'" do expect(PathHelper.dirname(PathHelper.join('', 'a', 'b', 'c'))).to eq(PathHelper.join('', 'a', 'b')) end end end end describe "validate_path" do context "on windows" do before(:each) do # pass by default allow(Chef::Platform).to receive(:windows?).and_return(true) allow(PathHelper).to receive(:printable?).and_return(true) allow(PathHelper).to receive(:windows_max_length_exceeded?).and_return(false) end it "returns the path if the path passes the tests" do expect(PathHelper.validate_path("C:\\ThisIsRigged")).to eql("C:\\ThisIsRigged") end it "does not raise an error if everything looks great" do expect { PathHelper.validate_path("C:\\cool path\\dude.exe") }.not_to raise_error end it "raises an error if the path has invalid characters" do allow(PathHelper).to receive(:printable?).and_return(false) expect { PathHelper.validate_path("Newline!\n") }.to raise_error(Chef::Exceptions::ValidationFailed) end it "Adds the \\\\?\\ prefix if the path exceeds MAX_LENGTH and does not have it" do long_path = "C:\\" + "a" * 250 + "\\" + "b" * 250 prefixed_long_path = "\\\\?\\" + long_path allow(PathHelper).to receive(:windows_max_length_exceeded?).and_return(true) expect(PathHelper.validate_path(long_path)).to eql(prefixed_long_path) end end end describe "windows_max_length_exceeded?" do it "returns true if the path is too long (259 + NUL) for the API" do expect(PathHelper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 6)).to be_truthy end it "returns false if the path is not too long (259 + NUL) for the standard API" do expect(PathHelper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 5)).to be_falsey end it "returns false if the path is over 259 characters but uses the \\\\?\\ prefix" do expect(PathHelper.windows_max_length_exceeded?("\\\\?\\C:\\" + "a" * 250 + "\\" + "b" * 250)).to be_falsey end end describe "printable?" do it "returns true if the string contains no non-printable characters" do expect(PathHelper.printable?("C:\\Program Files (x86)\\Microsoft Office\\Files.lst")).to be_truthy end it "returns true when given 'abc' in unicode" do expect(PathHelper.printable?("\u0061\u0062\u0063")).to be_truthy end it "returns true when given japanese unicode" do expect(PathHelper.printable?("\uff86\uff87\uff88")).to be_truthy end it "returns false if the string contains a non-printable character" do expect(PathHelper.printable?("\my files\work\notes.txt")).to be_falsey end # This isn't necessarily a requirement, but here to be explicit about functionality. it "returns false if the string contains a newline or tab" do expect(PathHelper.printable?("\tThere's no way,\n\t *no* way,\n\t that you came from my loins.\n")).to be_falsey end end describe "canonical_path" do context "on windows", :windows_only do it "returns an absolute path with backslashes instead of slashes" do expect(PathHelper.canonical_path("\\\\?\\C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini") end it "adds the \\\\?\\ prefix if it is missing" do expect(PathHelper.canonical_path("C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini") end it "returns a lowercase path" do expect(PathHelper.canonical_path("\\\\?\\C:\\CASE\\INSENSITIVE")).to eq("\\\\?\\c:\\case\\insensitive") end end context "not on windows", :unix_only do it "returns a canonical path" do expect(PathHelper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default") end end end describe "paths_eql?" do it "returns true if the paths are the same" do allow(PathHelper).to receive(:canonical_path).with("bandit").and_return("c:/bandit/bandit") allow(PathHelper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit") expect(PathHelper.paths_eql?("bandit", "../bandit/bandit")).to be_truthy end it "returns false if the paths are different" do allow(PathHelper).to receive(:canonical_path).with("bandit").and_return("c:/Bo/Bandit") allow(PathHelper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit") expect(PathHelper.paths_eql?("bandit", "../bandit/bandit")).to be_falsey end end describe "escape_glob" do it "escapes characters reserved by glob" do path = "C:\\this\\*path\\[needs]\\escaping?" escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?" expect(PathHelper.escape_glob(path)).to eq(escaped_path) end context "when given more than one argument" do it "joins, cleanpaths, and escapes characters reserved by glob" do args = ["this/*path", "[needs]", "escaping?"] escaped_path = if windows? "this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?" else "this/\\*path/\\[needs\\]/escaping\\?" end expect(PathHelper).to receive(:join).with(*args).and_call_original expect(PathHelper).to receive(:cleanpath).and_call_original expect(PathHelper.escape_glob(*args)).to eq(escaped_path) end end end describe "all_homes" do before do stub_const('ENV', env) allow(Chef::Platform).to receive(:windows?).and_return(is_windows) end context "on windows" do let (:is_windows) { true } end context "on unix" do let (:is_windows) { false } context "when HOME is not set" do let (:env) { {} } it "returns an empty array" do expect(PathHelper.all_homes).to eq([]) end end end end end chef-12.3.0/spec/unit/util/diff_spec.rb0000644000004100000410000004310312520074675017700 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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 'tmpdir' shared_context "using file paths with spaces" do let!(:old_tempfile) { Tempfile.new("chef-util diff-spec") } let!(:new_tempfile) { Tempfile.new("chef-util diff-spec") } end shared_context "using file paths without spaces" do let!(:old_tempfile) { Tempfile.new("chef-util-diff-spec") } let!(:new_tempfile) { Tempfile.new("chef-util-diff-spec") } end shared_examples_for "a diff util" do it "should return a Chef::Util::Diff" do expect(differ).to be_a_kind_of(Chef::Util::Diff) end it "produces a diff even if the old_file does not exist" do old_tempfile.close old_tempfile.unlink expect(differ.for_output).to eql(["(no diff)"]) end it "produces a diff even if the new_file does not exist" do new_tempfile.close new_tempfile.unlink expect(differ.for_output).to eql(["(no diff)"]) end describe "when the two files exist with no content" do it "calling for_output should return the error message" do expect(differ.for_output).to eql(["(no diff)"]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when diffs are disabled" do before do Chef::Config[:diff_disabled] = true end after do Chef::Config[:diff_disabled] = false end it "calling for_output should return the error message" do expect(differ.for_output).to eql( [ "(diff output suppressed by config)" ] ) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when the old_file has binary content" do before do old_tempfile.write("\x01\xff") old_tempfile.close end it "calling for_output should return the error message" do expect(differ.for_output).to eql( [ "(current file is binary, diff output suppressed)" ] ) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when the new_file has binary content" do before do new_tempfile.write("\x01\xff") new_tempfile.close end it "calling for_output should return the error message" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when the default external encoding is UTF-8" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::UTF_8 end after do Encoding.default_external = @saved_default_external end describe "when a file has ASCII text" do before do new_tempfile.write(plain_ascii) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end describe "when a file has UTF-8 text" do before do new_tempfile.write(utf_8) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end describe "when a file has Latin-1 text" do before do new_tempfile.write(latin_1) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when a file has Shift-JIS text" do before do new_tempfile.write(shift_jis) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end end describe "when the default external encoding is Latin-1" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::ISO_8859_1 end after do Encoding.default_external = @saved_default_external end describe "when a file has ASCII text" do before do new_tempfile.write(plain_ascii) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end describe "when a file has UTF-8 text" do before do new_tempfile.write(utf_8) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when a file has Latin-1 text" do before do new_tempfile.write(latin_1) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end describe "when a file has Shift-JIS text" do before do new_tempfile.write(shift_jis) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end end describe "when the default external encoding is Shift_JIS" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::Shift_JIS end after do Encoding.default_external = @saved_default_external end describe "when a file has ASCII text" do before do new_tempfile.write(plain_ascii) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end describe "when a file has UTF-8 text" do before do new_tempfile.write(utf_8) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when a file has Latin-1 text" do before do new_tempfile.write(latin_1) new_tempfile.close end it "calling for_output should complain that the content is binary" do expect(differ.for_output).to eql( [ "(new content is binary, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when a file has Shift-JIS text" do before do new_tempfile.write(shift_jis) new_tempfile.close end it "calling for_output should return a valid diff" do expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a utf-8 string" do expect(differ.for_reporting.encoding).to equal(Encoding::UTF_8) end end end describe "when testing the diff_filesize_threshold" do before do @diff_filesize_threshold_saved = Chef::Config[:diff_filesize_threshold] Chef::Config[:diff_filesize_threshold] = 10 end after do Chef::Config[:diff_filesize_threshold] = @diff_filesize_threshold_saved end describe "when the old_file goes over the threshold" do before do old_tempfile.write("But thats what you get when Wu-Tang raised you") old_tempfile.close end it "calling for_output should return the error message" do expect(differ.for_output).to eql( [ "(file sizes exceed 10 bytes, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end describe "when the new_file goes over the threshold" do before do new_tempfile.write("But thats what you get when Wu-Tang raised you") new_tempfile.close end it "calling for_output should return the error message" do expect(differ.for_output).to eql( [ "(file sizes exceed 10 bytes, diff output suppressed)" ]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end end describe "when generating a valid diff" do before do old_tempfile.write("foo") old_tempfile.close new_tempfile.write("bar") new_tempfile.close end it "calling for_output should return a unified diff" do expect(differ.for_output.size).to eql(5) expect(differ.for_output.join("\\n")).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end it "calling for_reporting should return a unified diff" do expect(differ.for_reporting).to match(/\A--- .*\\n\+\+\+ .*\\n@@/m) end describe "when the diff output is too long" do before do @diff_output_threshold_saved = Chef::Config[:diff_output_threshold] Chef::Config[:diff_output_threshold] = 10 end after do Chef::Config[:diff_output_threshold] = @diff_output_threshold_saved end it "calling for_output should return the error message" do expect(differ.for_output).to eql(["(long diff of over 10 characters, diff output suppressed)"]) end it "calling for_reporting should be nil" do expect(differ.for_reporting).to be_nil end end end describe "when checking if files are binary or text" do it "should identify zero-length files as text" do Tempfile.open("chef-util-diff-spec") do |file| file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify text files as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(plain_ascii) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify a null-terminated string files as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write("This is a binary file.\0") file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end it "should identify null-teriminated multi-line string files as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write("This is a binary file.\nNo Really\nit is\0") file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end describe "when the default external encoding is UTF-8" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::UTF_8 end after do Encoding.default_external = @saved_default_external end it "should identify normal ASCII as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(plain_ascii) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify UTF-8 as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(utf_8) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify Latin-1 that is invalid UTF-8 as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(latin_1) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end it "should identify Shift-JIS that is invalid UTF-8 as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(shift_jis) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end end describe "when the default external encoding is Latin-1" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::ISO_8859_1 end after do Encoding.default_external = @saved_default_external end it "should identify normal ASCII as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(plain_ascii) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify UTF-8 that is invalid Latin-1 as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(utf_8) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end it "should identify Latin-1 as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(latin_1) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify Shift-JIS that is invalid Latin-1 as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(shift_jis) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end end describe "when the default external encoding is Shift-JIS" do before do @saved_default_external = Encoding.default_external Encoding.default_external = Encoding::Shift_JIS end after do Encoding.default_external = @saved_default_external end it "should identify normal ASCII as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(plain_ascii) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end it "should identify UTF-8 that is invalid Shift-JIS as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(utf_8) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end it "should identify Latin-1 that is invalid Shift-JIS as binary" do Tempfile.open("chef-util-diff-spec") do |file| file.write(latin_1) file.close expect(differ.send(:is_binary?, file.path)).to be_truthy end end it "should identify Shift-JIS as text" do Tempfile.open("chef-util-diff-spec") do |file| file.write(shift_jis) file.close expect(differ.send(:is_binary?, file.path)).to be_falsey end end end end end describe Chef::Util::Diff, :uses_diff => true do let!(:old_file) { old_tempfile.path } let!(:new_file) { new_tempfile.path } let(:plain_ascii) { "This is a text file.\nWith more than one line.\nAnd a \tTab.\nAnd lets make sure that other printable chars work too: ~!@\#$%^&*()`:\"<>?{}|_+,./;'[]\\-=\n" } # these are all byte sequences that are illegal in the other encodings... (but they may legally transcode) let(:utf_8) { "testing utf-8 unicode...\n\n\non a new line: \xE2\x80\x93\n" } # unicode em-dash let(:latin_1) { "It is more metal.\nif you have an \xFDmlaut.\n" } # NB: changed to y-with-diaresis, but i'm American so I don't know the difference let(:shift_jis) { "I have no idea what this character is:\n \x83\x80.\n" } # seriously, no clue, but \x80 is nice and illegal in other encodings let(:differ) do # subject differ = Chef::Util::Diff.new differ.diff(old_file, new_file) differ end describe "when file path has spaces" do include_context "using file paths with spaces" it_behaves_like "a diff util" end describe "when file path doesn't have spaces" do include_context "using file paths without spaces" it_behaves_like "a diff util" end end chef-12.3.0/spec/unit/util/threaded_job_queue_spec.rb0000644000004100000410000000316712520074675022614 0ustar www-datawww-data# Copyright:: Copyright (c) 2014 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' class WorkerThreadError < StandardError end describe Chef::Util::ThreadedJobQueue do let(:queue) { Chef::Util::ThreadedJobQueue.new } it "should pass mutex to jobs with an arity of 1" do job = double() expect(job).to receive(:arity).at_least(:once).and_return(1) expect(job).to receive(:call).exactly(5).times.with(an_instance_of(Mutex)) 5.times { queue << job } queue.process end it "should pass nothing to jobs with an arity of 0" do job = double() expect(job).to receive(:arity).at_least(:once).and_return(0) expect(job).to receive(:call).exactly(5).times.with(no_args) 5.times { queue << job } queue.process end it "should use specified number of threads" do expect(Thread).to receive(:new).exactly(7).times.and_call_original queue.process(7) end it "should propagate exceptions to the main thread" do queue << lambda { raise WorkerThreadError } expect { queue.process }.to raise_error(WorkerThreadError) end end chef-12.3.0/spec/unit/util/file_edit_spec.rb0000644000004100000410000001431512520074675020717 0ustar www-datawww-data# # Author:: Nuo Yan () # 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' require 'tempfile' describe Chef::Util::FileEdit do let(:starting_content) do <<-EOF 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost EOF end let(:localhost_replaced) do <<-EOF 127.0.0.1 replacement 255.255.255.255 broadcasthost ::1 replacement fe80::1%lo0 replacement EOF end let(:localhost_line_replaced) do <<-EOF replacement line 255.255.255.255 broadcasthost replacement line replacement line EOF end let(:localhost_deleted) do # sensitive to deliberate trailing whitespace "127.0.0.1 \n255.255.255.255 broadcasthost\n::1 \nfe80::1%lo0 \n" end let(:localhost_line_deleted) do <<-EOF 255.255.255.255 broadcasthost EOF end let(:append_after_all_localhost) do <<-EOF 127.0.0.1 localhost new line inserted 255.255.255.255 broadcasthost ::1 localhost new line inserted fe80::1%lo0 localhost new line inserted EOF end let(:append_after_content) do <<-EOF 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost new line inserted EOF end let(:append_twice) do <<-EOF 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost once twice EOF end let(:target_file) do f = Tempfile.open('file_edit_spec') f.write(starting_content) f.close f end let(:fedit) { Chef::Util::FileEdit.new(target_file.path) } after(:each) do target_file.close! end describe "initialiize" do it "should create a new Chef::Util::FileEdit object" do expect(fedit).to be_instance_of(Chef::Util::FileEdit) end it "should throw an exception if the input file does not exist" do expect{Chef::Util::FileEdit.new("nonexistfile")}.to raise_error(ArgumentError) end # CHEF-5018: people have monkey patched this and it has accidentally been broken it "should read the contents into memory as an array" do expect(fedit.send(:editor).lines).to be_instance_of(Array) end end describe "when the file is blank" do let(:hosts_content) { "" } it "should not throw an exception" do expect{ fedit }.not_to raise_error end end def edited_file_contents IO.read(target_file.path) end describe "search_file_replace" do it "should accept regex passed in as a string (not Regexp object) and replace the match if there is one" do fedit.search_file_replace("localhost", "replacement") expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(localhost_replaced) end it "should accept regex passed in as a Regexp object and replace the match if there is one" do fedit.search_file_replace(/localhost/, "replacement") expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(localhost_replaced) end it "should do nothing if there isn't a match" do fedit.search_file_replace(/pattern/, "replacement") expect(fedit.unwritten_changes?).to be_falsey fedit.write_file expect(edited_file_contents).to eq(starting_content) end end describe "search_file_replace_line" do it "should search for match and replace the whole line" do fedit.search_file_replace_line(/localhost/, "replacement line") expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(localhost_line_replaced) end end describe "search_file_delete" do it "should search for match and delete the match" do fedit.search_file_delete(/localhost/) expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(localhost_deleted) end end describe "search_file_delete_line" do it "should search for match and delete the matching line" do fedit.search_file_delete_line(/localhost/) expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(localhost_line_deleted) end end describe "insert_line_after_match" do it "should search for match and insert the given line after the matching line" do fedit.insert_line_after_match(/localhost/, "new line inserted") expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(append_after_all_localhost) end end describe "insert_line_if_no_match" do it "should search for match and insert the given line if no line match" do fedit.insert_line_if_no_match(/pattern/, "new line inserted") expect(fedit.unwritten_changes?).to be_truthy fedit.write_file expect(edited_file_contents).to eq(append_after_content) end it "should do nothing if there is a match" do fedit.insert_line_if_no_match(/localhost/, "replacement") expect(fedit.unwritten_changes?).to be_falsey fedit.write_file expect(edited_file_contents).to eq(starting_content) end it "should work more than once" do fedit.insert_line_if_no_match(/missing/, "once") fedit.insert_line_if_no_match(/missing/, "twice") fedit.write_file expect(edited_file_contents).to eq(append_twice) end end describe "file_edited" do it "should return true if a file got edited" do fedit.insert_line_if_no_match(/pattern/, "new line inserted") fedit.write_file expect(fedit.file_edited?).to be_truthy end end end chef-12.3.0/spec/unit/util/selinux_spec.rb0000644000004100000410000001344412520074675020464 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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::Util::Selinux do class TestClass include Chef::Util::Selinux def self.reset_state @@selinux_enabled = nil @@restorecon_path = nil @@selinuxenabled_path = nil end end before do TestClass.reset_state @test_instance = TestClass.new end after(:each) do TestClass.reset_state end it "each part of ENV['PATH'] should be checked" do expected_paths = ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ] expected_paths.each do |bin_path| selinux_path = File.join(bin_path, "selinuxenabled") expect(File).to receive(:executable?).with(selinux_path).and_return(false) end expect(@test_instance.selinux_enabled?).to be_falsey end describe "when selinuxenabled binary exists" do before do @selinux_enabled_path = File.join("/sbin", "selinuxenabled") allow(File).to receive(:executable?) do |file_path| expect(file_path.end_with?("selinuxenabled")).to be_truthy file_path == @selinux_enabled_path end end describe "when selinux is enabled" do before do cmd_result = double("Cmd Result", :exitstatus => 0) expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, {:returns=>[0, 1]}).and_return(cmd_result) end it "should report selinux is enabled" do expect(@test_instance.selinux_enabled?).to be_truthy # should check the file system only once for multiple calls expect(@test_instance.selinux_enabled?).to be_truthy end end describe "when selinux is disabled" do before do cmd_result = double("Cmd Result", :exitstatus => 1) expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, {:returns=>[0, 1]}).and_return(cmd_result) end it "should report selinux is disabled" do expect(@test_instance.selinux_enabled?).to be_falsey # should check the file system only once for multiple calls expect(@test_instance.selinux_enabled?).to be_falsey end end describe "when selinux gives an unexpected status" do before do cmd_result = double("Cmd Result", :exitstatus => 101) expect(@test_instance).to receive(:shell_out!).once.with(@selinux_enabled_path, {:returns=>[0, 1]}).and_return(cmd_result) end it "should throw an error" do expect {@test_instance.selinux_enabled?}.to raise_error(RuntimeError) end end end describe "when selinuxenabled binary doesn't exist" do before do allow(File).to receive(:executable?) do |file_path| expect(file_path.end_with?("selinuxenabled")).to be_truthy false end end it "should report selinux is disabled" do expect(@test_instance.selinux_enabled?).to be_falsey # should check the file system only once for multiple calls expect(File).not_to receive(:executable?) expect(@test_instance.selinux_enabled?).to be_falsey end end describe "when restorecon binary exists on the system" do let (:path) { "/path/to/awesome directory" } before do @restorecon_enabled_path = File.join("/sbin", "restorecon") allow(File).to receive(:executable?) do |file_path| expect(file_path.end_with?("restorecon")).to be_truthy file_path == @restorecon_enabled_path end end it "should call restorecon non-recursive by default" do restorecon_command = "#{@restorecon_enabled_path} -R \"#{path}\"" expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command) @test_instance.restore_security_context(path) expect(File).not_to receive(:executable?) @test_instance.restore_security_context(path) end it "should call restorecon recursive when recursive is set" do restorecon_command = "#{@restorecon_enabled_path} -R -r \"#{path}\"" expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command) @test_instance.restore_security_context(path, true) expect(File).not_to receive(:executable?) @test_instance.restore_security_context(path, true) end it "should call restorecon non-recursive when recursive is not set" do restorecon_command = "#{@restorecon_enabled_path} -R \"#{path}\"" expect(@test_instance).to receive(:shell_out!).twice.with(restorecon_command) @test_instance.restore_security_context(path) expect(File).not_to receive(:executable?) @test_instance.restore_security_context(path) end describe "when restorecon doesn't exist on the system" do before do allow(File).to receive(:executable?) do |file_path| expect(file_path.end_with?("restorecon")).to be_truthy false end end it "should log a warning message" do log = [ ] allow(Chef::Log).to receive(:warn) do |message| log << message end @test_instance.restore_security_context(path) expect(log).not_to be_empty expect(File).not_to receive(:executable?) @test_instance.restore_security_context(path) end end end end chef-12.3.0/spec/unit/util/powershell/0000755000004100000410000000000012520074675017614 5ustar www-datawww-datachef-12.3.0/spec/unit/util/powershell/ps_credential_spec.rb0000644000004100000410000000261212520074675023770 0ustar www-datawww-data# # Author:: Jay Mundrawala # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/powershell/ps_credential' describe Chef::Util::Powershell::PSCredential do let (:username) { 'foo' } let (:password) { 'password' } context 'when username and password are provided' do let(:ps_credential) { Chef::Util::Powershell::PSCredential.new(username, password)} context 'when calling to_psobject' do it 'should create the script to create a PSCredential when calling' do allow(ps_credential).to receive(:encrypt).with(password).and_return('encrypted') expect(ps_credential.to_psobject).to eq( "New-Object System.Management.Automation.PSCredential("\ "'#{username}',('encrypted' | ConvertTo-SecureString))") end end end end chef-12.3.0/spec/unit/util/powershell/cmdlet_spec.rb0000644000004100000410000000674012520074675022432 0ustar www-datawww-data# # Author:: Jay Mundrawala # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/powershell/cmdlet' describe Chef::Util::Powershell::Cmdlet do before (:all) do @node = Chef::Node.new @cmdlet = Chef::Util::Powershell::Cmdlet.new(@node, 'Some-Commandlet') end describe '#validate_switch_name!' do it 'should not raise an error if a name contains all upper case letters' do @cmdlet.send(:validate_switch_name!, "HELLO") end it 'should not raise an error if the name contains all lower case letters' do @cmdlet.send(:validate_switch_name!, "hello") end it 'should not raise an error if no special characters are used except _' do @cmdlet.send(:validate_switch_name!, "hello_world") end %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym| it "raises an Argument error if it configuration name contains #{sym}" do expect { @cmdlet.send(:validate_switch_name!, "Hello#{sym}") }.to raise_error(ArgumentError) end end end describe '#escape_parameter_value' do # Is this list really complete? %w{` " # '}.each do |c| it "escapse #{c}" do expect(@cmdlet.send(:escape_parameter_value, "stuff #{c}")).to eql("stuff `#{c}") end end it 'does not do anything to a string without special characters' do expect(@cmdlet.send(:escape_parameter_value, 'stuff')).to eql('stuff') end end describe '#escape_string_parameter_value' do it "surrounds a string with ''" do expect(@cmdlet.send(:escape_string_parameter_value, 'stuff')).to eql("'stuff'") end end describe '#command_switches_string' do it 'raises an ArgumentError if the key is not a symbol' do expect { @cmdlet.send(:command_switches_string, {'foo' => 'bar'}) }.to raise_error(ArgumentError) end it 'does not allow invalid switch names' do expect { @cmdlet.send(:command_switches_string, {:foo! => 'bar'}) }.to raise_error(ArgumentError) end it 'ignores switches with a false value' do expect(@cmdlet.send(:command_switches_string, {foo: false})).to eql('') end it 'should correctly handle a value type of string' do expect(@cmdlet.send(:command_switches_string, {foo: 'bar'})).to eql("-foo 'bar'") end it 'should correctly handle a value type of string even when it is 0 length' do expect(@cmdlet.send(:command_switches_string, {foo: ''})).to eql("-foo ''") end it 'should not quote integers' do expect(@cmdlet.send(:command_switches_string, {foo: 1})).to eql("-foo 1") end it 'should not quote floats' do expect(@cmdlet.send(:command_switches_string, {foo: 1.0})).to eql("-foo 1.0") end it 'has just the switch when the value is true' do expect(@cmdlet.send(:command_switches_string, {foo: true})).to eql("-foo") end end end chef-12.3.0/spec/unit/util/backup_spec.rb0000644000004100000410000001210112520074675020227 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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 'tmpdir' describe Chef::Util::Backup do let (:tempfile) do Tempfile.new("chef-util-backup-spec-test") end before(:each) do @new_resource = double("new_resource") expect(@new_resource).to receive(:path).at_least(:once).and_return(tempfile.path) @backup = Chef::Util::Backup.new(@new_resource) end it "should store the resource passed to new as new_resource" do expect(@backup.new_resource).to eql(@new_resource) end describe "for cases when we don't want to back anything up" do before(:each) do expect(@backup).not_to receive(:do_backup) end it "should not attempt to backup a file if :backup is false" do expect(@new_resource).to receive(:backup).at_least(:once).and_return(false) @backup.backup! end it "should not attempt to backup a file if :backup == 0" do expect(@new_resource).to receive(:backup).at_least(:once).and_return(0) @backup.backup! end it "should not attempt to backup a file if it does not exist" do expect(@new_resource).to receive(:backup).at_least(:once).and_return(1) expect(File).to receive(:exist?).with(tempfile.path).at_least(:once).and_return(false) @backup.backup! end end describe "for cases when we want to back things up" do before(:each) do expect(@backup).to receive(:do_backup) end describe "when the number of backups is specified as 1" do before(:each) do expect(@new_resource).to receive(:backup).at_least(:once).and_return(1) end it "should not delete anything if this is the only backup" do expect(@backup).to receive(:sorted_backup_files).and_return(['a']) expect(@backup).not_to receive(:delete_backup) @backup.backup! end it "should keep only 1 backup copy" do expect(@backup).to receive(:sorted_backup_files).and_return(['a', 'b', 'c']) expect(@backup).to receive(:delete_backup).with('b') expect(@backup).to receive(:delete_backup).with('c') @backup.backup! end end describe "when the number of backups is specified as 2" do before(:each) do expect(@new_resource).to receive(:backup).at_least(:once).and_return(2) end it "should not delete anything if we only have one other backup" do expect(@backup).to receive(:sorted_backup_files).and_return(['a', 'b']) expect(@backup).not_to receive(:delete_backup) @backup.backup! end it "should keep only 2 backup copies" do expect(@backup).to receive(:sorted_backup_files).and_return(['a', 'b', 'c', 'd']) expect(@backup).to receive(:delete_backup).with('c') expect(@backup).to receive(:delete_backup).with('d') @backup.backup! end end end describe "backup_filename" do it "should return a timestamped path" do expect(@backup).to receive(:path).and_return('/a/b/c.txt') expect(@backup.send(:backup_filename)).to match(%r|^/a/b/c.txt.chef-\d{14}.\d{6}$|) end it "should strip the drive letter off for windows" do expect(@backup).to receive(:path).and_return('c:\a\b\c.txt') expect(@backup.send(:backup_filename)).to match(%r|^\\a\\b\\c.txt.chef-\d{14}.\d{6}$|) end it "should strip the drive letter off for windows (with forwardslashes)" do expect(@backup).to receive(:path).and_return('c:/a/b/c.txt') expect(@backup.send(:backup_filename)).to match(%r|^/a/b/c.txt.chef-\d{14}.\d{6}$|) end end describe "backup_path" do it "uses the file's directory when Chef::Config[:file_backup_path] is nil" do expect(@backup).to receive(:path).and_return('/a/b/c.txt') Chef::Config[:file_backup_path] = nil expect(@backup.send(:backup_path)).to match(%r|^/a/b/c.txt.chef-\d{14}.\d{6}$|) end it "uses the configured Chef::Config[:file_backup_path]" do expect(@backup).to receive(:path).and_return('/a/b/c.txt') Chef::Config[:file_backup_path] = '/backupdir' expect(@backup.send(:backup_path)).to match(%r|^/backupdir[\\/]+a/b/c.txt.chef-\d{14}.\d{6}$|) end it "uses the configured Chef::Config[:file_backup_path] and strips the drive on windows" do expect(@backup).to receive(:path).and_return('c:\\a\\b\\c.txt') Chef::Config[:file_backup_path] = 'c:\backupdir' expect(@backup.send(:backup_path)).to match(%r|^c:\\backupdir[\\/]+a\\b\\c.txt.chef-\d{14}.\d{6}$|) end end end chef-12.3.0/spec/unit/util/editor_spec.rb0000644000004100000410000001122012520074675020251 0ustar www-datawww-datarequire 'spec_helper' require 'chef/util/editor' describe Chef::Util::Editor do describe '#initialize' do it 'takes an Enumerable of lines' do editor = described_class.new(File.open(__FILE__)) expect(editor.lines).to be == IO.readlines(__FILE__) end it 'makes a copy of an Array' do array = Array.new editor = described_class.new(array) expect(editor.lines).to_not be(array) end end subject(:editor) { described_class.new(input_lines) } let(:input_lines) { ['one', 'two', 'two', 'three'] } describe '#append_line_after' do context 'when there is no match' do subject(:execute) { editor.append_line_after('missing', 'new') } it('returns the number of added lines') { is_expected.to eq(0) } it 'does not add any lines' do expect { execute }.to_not change { editor.lines } end end context 'when there is a match' do subject(:execute) { editor.append_line_after('two', 'new') } it('returns the number of added lines') { is_expected.to eq(2) } it 'adds a line after each match' do execute expect(editor.lines).to be == ['one', 'two', 'new', 'two', 'new', 'three'] end end it 'matches a Regexp' do expect(editor.append_line_after(/^ee/, 'new')).to be == 0 expect(editor.append_line_after(/ee$/, 'new')).to be == 1 end end describe '#append_line_if_missing' do context 'when there is no match' do subject(:execute) { editor.append_line_if_missing('missing', 'new') } it('returns the number of added lines') { is_expected.to eq(1) } it 'adds a line to the end' do execute expect(editor.lines).to be == ['one', 'two', 'two', 'three', 'new'] end end context 'when there is a match' do subject(:execute) { editor.append_line_if_missing('one', 'new') } it('returns the number of added lines') { is_expected.to eq(0) } it 'does not add any lines' do expect { execute }.to_not change { editor.lines } end end it 'matches a Regexp' do expect(editor.append_line_if_missing(/ee$/, 'new')).to be == 0 expect(editor.append_line_if_missing(/^ee/, 'new')).to be == 1 end end describe '#remove_lines' do context 'when there is no match' do subject(:execute) { editor.remove_lines('missing') } it('returns the number of removed lines') { is_expected.to eq(0) } it 'does not remove any lines' do expect { execute }.to_not change { editor.lines } end end context 'when there is a match' do subject(:execute) { editor.remove_lines('two') } it('returns the number of removed lines') { is_expected.to eq(2) } it 'removes the matching lines' do execute expect(editor.lines).to be == ['one', 'three'] end end it 'matches a Regexp' do expect(editor.remove_lines(/^ee/)).to be == 0 expect(editor.remove_lines(/ee$/)).to be == 1 end end describe '#replace' do context 'when there is no match' do subject(:execute) { editor.replace('missing', 'new') } it('returns the number of changed lines') { is_expected.to eq(0) } it 'does not change any lines' do expect { execute }.to_not change { editor.lines } end end context 'when there is a match' do subject(:execute) { editor.replace('two', 'new') } it('returns the number of changed lines') { is_expected.to eq(2) } it 'replaces the matching portions' do execute expect(editor.lines).to be == ['one', 'new', 'new', 'three'] end end it 'matches a Regexp' do expect(editor.replace(/^ee/, 'new')).to be == 0 expect(editor.replace(/ee$/, 'new')).to be == 1 expect(editor.lines).to be == ['one', 'two', 'two', 'thrnew'] end end describe '#replace_lines' do context 'when there is no match' do subject(:execute) { editor.replace_lines('missing', 'new') } it('returns the number of changed lines') { is_expected.to eq(0) } it 'does not change any lines' do expect { execute }.to_not change { editor.lines } end end context 'when there is a match' do subject(:execute) { editor.replace_lines('two', 'new') } it('returns the number of replaced lines') { is_expected.to eq(2) } it 'replaces the matching line' do execute expect(editor.lines).to be == ['one', 'new', 'new', 'three'] end end it 'matches a Regexp' do expect(editor.replace_lines(/^ee/, 'new')).to be == 0 expect(editor.replace_lines(/ee$/, 'new')).to be == 1 expect(editor.lines).to be == ['one', 'two', 'two', 'new'] end end end chef-12.3.0/spec/unit/util/dsc/0000755000004100000410000000000012520074675016201 5ustar www-datawww-datachef-12.3.0/spec/unit/util/dsc/lcm_output_parser_spec.rb0000644000004100000410000001551112520074675023312 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/dsc/lcm_output_parser' describe Chef::Util::DSC::LocalConfigurationManager::Parser do context 'empty input parameter' do it 'raises an exception when there are no valid lines' do str = <<-EOF EOF expect {Chef::Util::DSC::LocalConfigurationManager::Parser::parse(str)}.to raise_error(Chef::Exceptions::LCMParser) end it 'raises an exception for a nil input' do expect {Chef::Util::DSC::LocalConfigurationManager::Parser::parse(nil)}.to raise_error(Chef::Exceptions::LCMParser) end end context 'correctly formatted output from lcm' do it 'returns a single resource when only 1 logged with the correct name' do str = < # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/dsc/configuration_generator' describe Chef::Util::DSC::ConfigurationGenerator do let(:conf_man) do node = Chef::Node.new Chef::Util::DSC::ConfigurationGenerator.new(node, 'tmp') end describe '#validate_configuration_name!' do it 'should not raise an error if a name contains all upper case letters' do conf_man.send(:validate_configuration_name!, "HELLO") end it 'should not raise an error if the name contains all lower case letters' do conf_man.send(:validate_configuration_name!, "hello") end it 'should not raise an error if no special characters are used except _' do conf_man.send(:validate_configuration_name!, "hello_world") end %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym| it "raises an Argument error if it configuration name contains #{sym}" do expect { conf_man.send(:validate_configuration_name!, "Hello#{sym}") }.to raise_error(ArgumentError) end end end describe "#get_merged_configuration_flags" do context 'when strings are used as switches' do it 'should merge the hash if there are no restricted switches' do merged = conf_man.send(:get_merged_configuration_flags!, {'flag' => 'a'}, 'hello') expect(merged).to include(:flag) expect(merged[:flag]).to eql('a') expect(merged).to include(:outputpath) end it 'should raise an ArgumentError if you try to override outputpath' do expect { conf_man.send(:get_merged_configuration_flags!, {'outputpath' => 'a'}, 'hello') }.to raise_error(ArgumentError) end it 'should be case insensitive for switches that are not allowed' do expect { conf_man.send(:get_merged_configuration_flags!, {'OutputPath' => 'a'}, 'hello') }.to raise_error(ArgumentError) end it 'should be case insensitive to switches that are allowed' do merged = conf_man.send(:get_merged_configuration_flags!, {'FLAG' => 'a'}, 'hello') expect(merged).to include(:flag) end end context 'when symbols are used as switches' do it 'should merge the hash if there are no restricted switches' do merged = conf_man.send(:get_merged_configuration_flags!, {:flag => 'a'}, 'hello') expect(merged).to include(:flag) expect(merged[:flag]).to eql('a') expect(merged).to include(:outputpath) end it 'should raise an ArgumentError if you try to override outputpath' do expect { conf_man.send(:get_merged_configuration_flags!, {:outputpath => 'a'}, 'hello') }.to raise_error(ArgumentError) end it 'should be case insensitive for switches that are not allowed' do expect { conf_man.send(:get_merged_configuration_flags!, {:OutputPath => 'a'}, 'hello') }.to raise_error(ArgumentError) end it 'should be case insensitive to switches that are allowed' do merged = conf_man.send(:get_merged_configuration_flags!, {:FLAG => 'a'}, 'hello') expect(merged).to include(:flag) end end context 'when there are no flags' do it 'should supply an output path if configuration_flags is an empty hash' do merged = conf_man.send(:get_merged_configuration_flags!, {}, 'hello') expect(merged).to include(:outputpath) expect(merged.length).to eql(1) end it 'should supply an output path if configuration_flags is an empty hash' do merged = conf_man.send(:get_merged_configuration_flags!, nil, 'hello') expect(merged).to include(:outputpath) expect(merged.length).to eql(1) end end # What should happen if configuration flags contains duplicates? # flagA => 'a', flaga => 'a' # or # flagA => 'a', flaga => 'b' # end describe '#write_document_generation_script' do let(:file_like_object) { double("file like object") } it "should write the input to a file" do allow(File).to receive(:open).and_yield(file_like_object) allow(File).to receive(:join) do |a, b| [a,b].join("++") end allow(file_like_object).to receive(:write) conf_man.send(:write_document_generation_script, 'file', 'hello', {}) expect(file_like_object).to have_received(:write) end end describe "#find_configuration_document" do it "should find the mof file" do # These tests seem way too implementation specific. Unfortunately, File and Dir # need to be mocked because they are OS specific allow(File).to receive(:join) do |a, b| [a,b].join("++") end allow(Dir).to receive(:entries).with("tmp++hello") {['f1', 'f2', 'hello.mof', 'f3']} expect(conf_man.send(:find_configuration_document, 'hello')).to eql('tmp++hello++hello.mof') end it "should return nil if the mof file is not found" do allow(File).to receive(:join) do |a, b| [a,b].join("++") end allow(Dir).to receive(:entries).with("tmp++hello") {['f1', 'f2', 'f3']} expect(conf_man.send(:find_configuration_document, 'hello')).to be_nil end end describe "#configuration_code" do it "should build dsc" do dsc = conf_man.send(:configuration_code, 'archive{}', 'hello', {}) found_configuration = false dsc.split(';').each do |command| if command.downcase =~ /\s*configuration\s+'hello'\s*\{\s*node\s+'localhost'\s*\{\s*archive\s*\{\s*\}\s*\}\s*\}\s*/ found_configuration = true end end expect(found_configuration).to be_truthy end context "with imports" do it "should import all resources when a module has an empty list" do dsc = conf_man.send(:configuration_code, 'archive{}', 'hello', {'FooModule' => []}) expect(dsc).to match(/Import-DscResource -ModuleName FooModule\s*\n/) end it "should import all resources when a module has a list with *" do dsc = conf_man.send(:configuration_code, 'archive{}', 'hello', {'FooModule' => ['FooResource', '*', 'BarResource']}) expect(dsc).to match(/Import-DscResource -ModuleName FooModule\s*\n/) end it "should import specific resources when a module has list without * that is not empty" do dsc = conf_man.send(:configuration_code, 'archive{}', 'hello', {'FooModule' => ['FooResource', 'BarResource']}) expect(dsc).to match(/Import-DscResource -ModuleName FooModule -Name FooResource,BarResource/) end it "should import multiple modules with multiple import statements" do dsc = conf_man.send(:configuration_code, 'archive{}', 'hello', {'FooModule' => ['FooResource', 'BarResource'], 'BazModule' => []}) expect(dsc).to match(/Import-DscResource -ModuleName FooModule -Name FooResource,BarResource/) expect(dsc).to match(/Import-DscResource -ModuleName BazModule\s*\n/) end end end end chef-12.3.0/spec/unit/util/dsc/local_configuration_manager_spec.rb0000644000004100000410000001504212520074675025255 0ustar www-datawww-data# # Author:: Adam Edwards # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/dsc/local_configuration_manager' describe Chef::Util::DSC::LocalConfigurationManager do let(:lcm) { Chef::Util::DSC::LocalConfigurationManager.new(nil, 'tmp') } let(:normal_lcm_output) { <<-EOH logtype: [machinename]: LCM: [ Start Set ] logtype: [machinename]: LCM: [ Start Resource ] [name] logtype: [machinename]: LCM: [ End Resource ] [name] logtype: [machinename]: LCM: [ End Set ] EOH } let(:no_whatif_lcm_output) { <<-EOH Start-DscConfiguration : A parameter cannot be found\r\n that matches parameter name 'whatif'. At line:1 char:123 + run-somecommand -whatif + ~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException + FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand EOH } let(:dsc_resource_import_failure_output) { <<-EOH PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . The SendConfigurationApply function did not succeed. + CategoryInfo : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException + FullyQualifiedErrorId : MI RESULT 1 + PSComputerName : . EOH } let(:lcm_status) { double("LCM cmdlet status", :stderr => lcm_standard_error, :return_value => lcm_standard_output, :succeeded? => lcm_cmdlet_success) } describe 'test_configuration method invocation' do context 'when interacting with the LCM using a PowerShell cmdlet' do before(:each) do allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status) end context 'that returns successfully' do before(:each) do allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status) end let(:lcm_standard_output) { normal_lcm_output } let(:lcm_standard_error) { nil } let(:lcm_cmdlet_success) { true } it 'should successfully return resource information for normally formatted output when cmdlet the cmdlet succeeds' do test_configuration_result = lcm.test_configuration('config', {}) expect(test_configuration_result.class).to be(Array) expect(test_configuration_result.length).to be > 0 expect(Chef::Log).not_to receive(:warn) end end context 'that fails due to missing what-if switch in DSC resource cmdlet implementation' do let(:lcm_standard_output) { '' } let(:lcm_standard_error) { no_whatif_lcm_output } let(:lcm_cmdlet_success) { false } it 'returns true when passed to #whatif_not_supported?' do expect(lcm.send(:whatif_not_supported?, no_whatif_lcm_output)).to be_truthy end it 'should should return a (possibly empty) array of ResourceInfo instances' do expect(Chef::Log).to receive(:warn).at_least(:once) expect(lcm).to receive(:whatif_not_supported?).and_call_original test_configuration_result = nil expect {test_configuration_result = lcm.test_configuration('config', {})}.not_to raise_error expect(test_configuration_result.class).to be(Array) end end context 'that fails due to a DSC resource not being imported before StartDSCConfiguration -whatif is executed' do let(:lcm_standard_output) { '' } let(:lcm_standard_error) { dsc_resource_import_failure_output } let(:lcm_cmdlet_success) { false } it 'should log a warning if the message is formatted as expected when a resource import failure occurs' do expect(Chef::Log).to receive(:warn).at_least(:once) expect(lcm).to receive(:dsc_module_import_failure?).and_call_original test_configuration_result = nil expect {test_configuration_result = lcm.test_configuration('config', {})}.not_to raise_error end it 'should return a (possibly empty) array of ResourceInfo instances' do expect(Chef::Log).to receive(:warn).at_least(:once) test_configuration_result = nil expect {test_configuration_result = lcm.test_configuration('config', {})}.not_to raise_error expect(test_configuration_result.class).to be(Array) end end context 'that fails due to an unknown PowerShell cmdlet error' do let(:lcm_standard_output) { 'some output' } let(:lcm_standard_error) { 'Abort, Retry, Fail?' } let(:lcm_cmdlet_success) { false } it 'should log a warning' do expect(Chef::Log).to receive(:warn).at_least(:once) expect(lcm).to receive(:dsc_module_import_failure?).and_call_original expect {lcm.test_configuration('config', {})}.not_to raise_error end end end it 'should identify a correctly formatted error message as a resource import failure' do expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output)).to be(true) end it 'should not identify an incorrectly formatted error message as a resource import failure' do expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output.gsub('module', 'gibberish'))).to be(false) end it 'should not identify a message without a CimException reference as a resource import failure' do expect(lcm.send(:dsc_module_import_failure?, dsc_resource_import_failure_output.gsub('CimException', 'ArgumentException'))).to be(false) end end end chef-12.3.0/spec/unit/util/dsc/resource_store.rb0000644000004100000410000000502312520074675021571 0ustar www-datawww-data# # Author:: Jay Mundrawala # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef' require 'chef/util/dsc/resource_store' describe Chef::Util::DSC::ResourceStore do let(:resource_store) { Chef::Util::DSC::ResourceStore.new } let(:resource_a) { { 'ResourceType' => 'AFoo', 'Name' => 'Foo', 'Module' => {'Name' => 'ModuleA'} } } let(:resource_b) { { 'ResourceType' => 'BFoo', 'Name' => 'Foo', 'Module' => {'Name' => 'ModuleB'} } } context 'when resources are not cached' do context 'when calling #resources' do it 'returns an empty array' do expect(resource_store.resources).to eql([]) end end context 'when calling #find' do it 'returns an empty list if it cannot find any matching resources' do expect(resource_store).to receive(:query_resource).and_return([]) expect(resource_store.find('foo')).to eql([]) end it 'returns the resource if it is found (comparisons are case insensitive)' do expect(resource_store).to receive(:query_resource).and_return([resource_a]) expect(resource_store.find('foo')).to eql([resource_a]) end it 'returns multiple resoures if they are found' do expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_b]) expect(resource_store.find('foo')).to include(resource_a, resource_b) end it 'deduplicates resources by ResourceName' do expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_a]) resource_store.find('foo') expect(resource_store.resources).to eq([resource_a]) end end end context 'when resources are cached' do it 'recalls resources from the cache if present' do expect(resource_store).not_to receive(:query_resource) expect(resource_store).to receive(:resources).and_return([resource_a]) resource_store.find('foo') end end end chef-12.3.0/spec/unit/provider_spec.rb0000644000004100000410000001313612520074675017650 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' class NoWhyrunDemonstrator < Chef::Provider attr_reader :system_state_altered def whyrun_supported? false end def load_current_resource end def action_foo @system_state_altered = true end end class ConvergeActionDemonstrator < Chef::Provider attr_reader :system_state_altered def whyrun_supported? true end def load_current_resource end def action_foo converge_by("running a state changing action") do @system_state_altered = true end end end describe Chef::Provider do before(:each) do @cookbook_collection = Chef::CookbookCollection.new([]) @node = Chef::Node.new @node.name "latte" @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events) @resource = Chef::Resource.new("funk", @run_context) @resource.cookbook_name = "a_delicious_pie" @provider = Chef::Provider.new(@resource, @run_context) end it "should mixin shell_out" do expect(@provider.respond_to?(:shell_out)).to be true end it "should mixin shell_out!" do expect(@provider.respond_to?(:shell_out!)).to be true end it "should mixin shell_out_with_systems_locale" do expect(@provider.respond_to?(:shell_out_with_systems_locale)).to be true end it "should store the resource passed to new as new_resource" do expect(@provider.new_resource).to eql(@resource) end it "should store the node passed to new as node" do expect(@provider.node).to eql(@node) end it "should have nil for current_resource by default" do expect(@provider.current_resource).to eql(nil) end it "should not support whyrun by default" do expect(@provider.send(:whyrun_supported?)).to eql(false) end it "should return true for action_nothing" do expect(@provider.action_nothing).to eql(true) end it "evals embedded recipes with a pristine resource collection" do @provider.run_context.instance_variable_set(:@resource_collection, "doesn't matter what this is") temporary_collection = nil snitch = Proc.new {temporary_collection = @run_context.resource_collection} @provider.send(:recipe_eval, &snitch) expect(temporary_collection).to be_an_instance_of(Chef::ResourceCollection) expect(@provider.run_context.instance_variable_get(:@resource_collection)).to eq("doesn't matter what this is") end it "does not re-load recipes when creating the temporary run context" do # we actually want to test that RunContext#load is never called, but we # can't stub all instances of an object with rspec's mocks. :/ allow(Chef::RunContext).to receive(:new).and_raise("not supposed to happen") snitch = Proc.new {temporary_collection = @run_context.resource_collection} @provider.send(:recipe_eval, &snitch) end context "when no converge actions are queued" do before do allow(@provider).to receive(:whyrun_supported?).and_return(true) allow(@provider).to receive(:load_current_resource) end it "does not mark the new resource as updated" do expect(@resource).not_to be_updated expect(@resource).not_to be_updated_by_last_action end end context "when converge actions have been added to the queue" do describe "and provider supports whyrun mode" do before do @provider = ConvergeActionDemonstrator.new(@resource, @run_context) end it "should tell us that it does support whyrun" do expect(@provider).to be_whyrun_supported end it "queues up converge actions" do @provider.action_foo expect(@provider.send(:converge_actions).actions.size).to eq(1) end it "executes pending converge actions to converge the system" do @provider.run_action(:foo) expect(@provider.instance_variable_get(:@system_state_altered)).to be_truthy end it "marks the resource as updated" do @provider.run_action(:foo) expect(@resource).to be_updated expect(@resource).to be_updated_by_last_action end end describe "and provider does not support whyrun mode" do before do Chef::Config[:why_run] = true @provider = NoWhyrunDemonstrator.new(@resource, @run_context) end after do Chef::Config[:why_run] = false end it "should tell us that it doesn't support whyrun" do expect(@provider).not_to be_whyrun_supported end it "should automatically generate a converge_by block on the provider's behalf" do @provider.run_action(:foo) expect(@provider.send(:converge_actions).actions.size).to eq(0) expect(@provider.system_state_altered).to be_falsey end it "should automatically execute the generated converge_by block" do @provider.run_action(:foo) expect(@provider.system_state_altered).to be_falsey expect(@resource).not_to be_updated expect(@resource).not_to be_updated_by_last_action end end end end chef-12.3.0/spec/unit/pure_application_spec.rb0000644000004100000410000000210112520074675021342 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 Chef 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. # This spec file intentionally doesn't include spec_helper.rb to # be able to test only Chef::Application. # Regression test for CHEF-5169 require 'chef/application' describe "Chef::Application" do let(:app) { Chef::Application.new } describe "load_config_file" do it "calls ConfigFetcher successfully without NameError" do expect { app.load_config_file }.not_to raise_error end end end chef-12.3.0/spec/unit/shell_spec.rb0000644000004100000410000001224012520074675017120 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 "ostruct" ObjectTestHarness = Proc.new do extend Shell::Extensions::ObjectCoreExtensions def conf=(new_conf) @conf = new_conf end def conf @conf end desc "rspecin'" def rspec_method end end class TestJobManager attr_accessor :jobs end describe Shell do before do Shell.irb_conf = {} allow(Shell::ShellSession.instance).to receive(:reset!) allow(Chef::Platform).to receive(:windows?).and_return(false) allow(Chef::Util::PathHelper).to receive(:home).and_return('/home/foo') end describe "reporting its status" do it "always says it is running" do expect(Shell).to be_running end end describe "configuring IRB" do it "configures irb history" do Shell.configure_irb expect(Shell.irb_conf[:HISTORY_FILE]).to eq(Chef::Util::PathHelper.home('.chef', 'chef_shell_history')) expect(Shell.irb_conf[:SAVE_HISTORY]).to eq(1000) end it "has a prompt like ``chef > '' in the default context" do Shell.configure_irb conf = OpenStruct.new conf.main = Object.new conf.main.instance_eval(&ObjectTestHarness) Shell.irb_conf[:IRB_RC].call(conf) expect(conf.prompt_c).to eq("chef > ") expect(conf.return_format).to eq(" => %s \n") expect(conf.prompt_i).to eq("chef > ") expect(conf.prompt_n).to eq("chef ?> ") expect(conf.prompt_s).to eq("chef%l> ") expect(conf.use_tracer).to eq(false) end it "has a prompt like ``chef:recipe > '' in recipe context" do Shell.configure_irb conf = OpenStruct.new events = Chef::EventDispatch::Dispatcher.new conf.main = Chef::Recipe.new(nil,nil,Chef::RunContext.new(Chef::Node.new, {}, events)) Shell.irb_conf[:IRB_RC].call(conf) expect(conf.prompt_c).to eq("chef:recipe > ") expect(conf.prompt_i).to eq("chef:recipe > ") expect(conf.prompt_n).to eq("chef:recipe ?> ") expect(conf.prompt_s).to eq("chef:recipe%l> ") end it "has a prompt like ``chef:attributes > '' in attributes/node context" do Shell.configure_irb conf = OpenStruct.new conf.main = Chef::Node.new Shell.irb_conf[:IRB_RC].call(conf) expect(conf.prompt_c).to eq("chef:attributes > ") expect(conf.prompt_i).to eq("chef:attributes > ") expect(conf.prompt_n).to eq("chef:attributes ?> ") expect(conf.prompt_s).to eq("chef:attributes%l> ") end end describe "convenience macros for creating the chef object" do before do @chef_object = Object.new @chef_object.instance_eval(&ObjectTestHarness) end it "creates help text for methods with descriptions" do expect(@chef_object.help_descriptions).to eq([Shell::Extensions::Help.new("rspec_method", "rspecin'", nil)]) end it "adds help text when a new method is described then defined" do describe_define =<<-EVAL desc "foo2the Bar" def baz end EVAL @chef_object.instance_eval describe_define expect(@chef_object.help_descriptions).to eq([Shell::Extensions::Help.new("rspec_method", "rspecin'"), Shell::Extensions::Help.new("baz", "foo2the Bar")]) end it "adds help text for subcommands" do describe_define =<<-EVAL subcommands :baz_obj_command => "something you can do with baz.baz_obj_command" def baz end EVAL @chef_object.instance_eval describe_define expected_help_text_fragments = [Shell::Extensions::Help.new("rspec_method", "rspecin'")] expected_help_text_fragments << Shell::Extensions::Help.new("baz.baz_obj_command", "something you can do with baz.baz_obj_command") expect(@chef_object.help_descriptions).to eq(expected_help_text_fragments) end it "doesn't add previous subcommand help to commands defined afterward" do describe_define =<<-EVAL desc "swingFromTree" def monkey_time end def super_monkey_time end EVAL @chef_object.instance_eval describe_define expect(@chef_object.help_descriptions.size).to eq(2) expect(@chef_object.help_descriptions.select {|h| h.cmd == "super_monkey_time" }).to be_empty end it "creates a help banner with the command descriptions" do expect(@chef_object.help_banner).to match(/^\|\ Command[\s]+\|\ Description[\s]*$/) expect(@chef_object.help_banner).to match(/^\|\ rspec_method[\s]+\|\ rspecin\'[\s]*$/) end end end chef-12.3.0/spec/unit/api_client/0000755000004100000410000000000012520074675016562 5ustar www-datawww-datachef-12.3.0/spec/unit/api_client/registration_spec.rb0000644000004100000410000002336612520074675022645 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'tempfile' require 'chef/api_client/registration' describe Chef::ApiClient::Registration do let(:key_location) do make_tmpname("client-registration-key") end let(:client_name) { "silent-bob" } subject(:registration) { Chef::ApiClient::Registration.new(client_name, key_location) } let(:private_key_data) do File.open(Chef::Config[:validation_key], "r") {|f| f.read.chomp } end let(:http_mock) { double("Chef::REST mock") } let(:expected_post_data) do { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem } end let(:expected_put_data) do { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem } end let(:server_v10_response) do {"uri" => "https://chef.local/clients/#{client_name}", "private_key" => "--begin rsa key etc--"} end # Server v11 includes `json_class` on all replies let(:server_v11_response) do response = Chef::ApiClient.new response.name(client_name) response.private_key("--begin rsa key etc--") response end let(:response_409) { Net::HTTPConflict.new("1.1", "409", "Conflict") } let(:exception_409) { Net::HTTPServerException.new("409 conflict", response_409) } let(:generated_private_key_pem) { IO.read(File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)) } let(:generated_private_key) { OpenSSL::PKey::RSA.new(generated_private_key_pem) } let(:generated_public_key) { generated_private_key.public_key } let(:create_with_pkey_response) do { "uri" => "", "public_key" => generated_public_key.to_pem } end let(:update_with_pkey_response) do {"name"=>client_name, "admin"=>false, "public_key"=> generated_public_key, "validator"=>false, "private_key"=>false, "clientname"=>client_name} end before do Chef::Config[:validation_client_name] = "test-validator" Chef::Config[:validation_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA) allow(OpenSSL::PKey::RSA).to receive(:generate).with(2048).and_return(generated_private_key) end after do File.unlink(key_location) if File.exist?(key_location) end it "has an HTTP client configured with validator credentials" do expect(registration.http_api).to be_a_kind_of(Chef::REST) expect(registration.http_api.client_name).to eq("test-validator") expect(registration.http_api.signing_key).to eq(private_key_data) end describe "when creating/updating the client on the server" do before do allow(registration).to receive(:http_api).and_return(http_mock) end it "posts a locally generated public key to the server to create a client" do expect(http_mock).to receive(:post). with("clients", expected_post_data). and_return(create_with_pkey_response) expect(registration.create_or_update).to eq(create_with_pkey_response) expect(registration.private_key).to eq(generated_private_key_pem) end it "puts a locally generated public key to the server to update a client" do expect(http_mock).to receive(:post). with("clients", expected_post_data). and_raise(exception_409) expect(http_mock).to receive(:put). with("clients/#{client_name}", expected_put_data). and_return(update_with_pkey_response) expect(registration.create_or_update).to eq(update_with_pkey_response) expect(registration.private_key).to eq(generated_private_key_pem) end it "writes the generated private key to disk" do expect(http_mock).to receive(:post). with("clients", expected_post_data). and_return(create_with_pkey_response) registration.run expect(IO.read(key_location)).to eq(generated_private_key_pem) end context "and the client already exists on a Chef 11 server" do it "requests a new key from the server and saves it" do expect(http_mock).to receive(:post).and_raise(exception_409) expect(http_mock).to receive(:put). with("clients/#{client_name}", expected_put_data). and_return(update_with_pkey_response) expect(registration.create_or_update).to eq(update_with_pkey_response) expect(registration.private_key).to eq(generated_private_key_pem) end end context "when local key generation is disabled" do let(:expected_post_data) do { :name => client_name, :admin => false } end let(:expected_put_data) do { :name => client_name, :admin => false, :private_key => true } end before do Chef::Config[:local_key_generation] = false expect(OpenSSL::PKey::RSA).not_to receive(:generate) end it "creates a new ApiClient on the server using the validator identity" do expect(http_mock).to receive(:post). with("clients", expected_post_data). and_return(server_v10_response) expect(registration.create_or_update).to eq(server_v10_response) expect(registration.private_key).to eq("--begin rsa key etc--") end context "and the client already exists on a Chef 11 server" do it "requests a new key from the server and saves it" do expect(http_mock).to receive(:post).and_raise(exception_409) expect(http_mock).to receive(:put). with("clients/#{client_name}", expected_put_data). and_return(server_v11_response) expect(registration.create_or_update).to eq(server_v11_response) expect(registration.private_key).to eq("--begin rsa key etc--") end end context "and the client already exists on a Chef 10 server" do it "requests a new key from the server and saves it" do expect(http_mock).to receive(:post).with("clients", expected_post_data). and_raise(exception_409) expect(http_mock).to receive(:put). with("clients/#{client_name}", expected_put_data). and_return(server_v10_response) expect(registration.create_or_update).to eq(server_v10_response) expect(registration.private_key).to eq("--begin rsa key etc--") end end end end describe "when writing the private key to disk" do before do allow(registration).to receive(:private_key).and_return('--begin rsa key etc--') end # Permission read via File.stat is busted on windows, though creating the # file with 0600 has the desired effect of giving access rights to the # owner only. A platform-specific functional test would be helpful. it "creates the file with 0600 permissions", :unix_only do expect(File).not_to exist(key_location) registration.write_key expect(File).to exist(key_location) stat = File.stat(key_location) expect(stat.mode & 07777).to eq(0600) end it "writes the private key content to the file" do registration.write_key expect(IO.read(key_location)).to eq("--begin rsa key etc--") end context 'when the client key location is a symlink' do it 'does not follow the symlink', :unix_only do expected_flags = (File::CREAT|File::TRUNC|File::RDWR) if defined?(File::NOFOLLOW) expected_flags |= File::NOFOLLOW end expect(registration.file_flags).to eq(expected_flags) end context 'with follow_client_key_symlink set to true' do before do Chef::Config[:follow_client_key_symlink] = true end it 'follows the symlink', :unix_only do expect(registration.file_flags).to eq(File::CREAT|File::TRUNC|File::RDWR) end end end end describe "when registering a client" do before do allow(registration).to receive(:http_api).and_return(http_mock) end it "creates the client on the server and writes the key" do expect(http_mock).to receive(:post).ordered.and_return(server_v10_response) registration.run expect(IO.read(key_location)).to eq(generated_private_key_pem) end it "retries up to 5 times" do response_500 = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error") exception_500 = Net::HTTPFatalError.new("500 Internal Server Error", response_500) expect(http_mock).to receive(:post).ordered.and_raise(exception_500) # 1 expect(http_mock).to receive(:post).ordered.and_raise(exception_500) # 2 expect(http_mock).to receive(:post).ordered.and_raise(exception_500) # 3 expect(http_mock).to receive(:post).ordered.and_raise(exception_500) # 4 expect(http_mock).to receive(:post).ordered.and_raise(exception_500) # 5 expect(http_mock).to receive(:post).ordered.and_return(server_v10_response) registration.run expect(IO.read(key_location)).to eq(generated_private_key_pem) end it "gives up retrying after the max attempts" do response_500 = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error") exception_500 = Net::HTTPFatalError.new("500 Internal Server Error", response_500) expect(http_mock).to receive(:post).exactly(6).times.and_raise(exception_500) expect {registration.run}.to raise_error(Net::HTTPFatalError) end end end chef-12.3.0/spec/unit/scan_access_control_spec.rb0000644000004100000410000001327412520074675022026 0ustar www-datawww-data# Author:: Daniel DeLeo () # 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 File.expand_path("../../spec_helper", __FILE__) require 'chef/scan_access_control' describe Chef::ScanAccessControl do before do @new_resource = Chef::Resource::File.new("/tmp/foo/bar/baz/link") @real_file = "/tmp/foo/bar/real/file" @current_resource = Chef::Resource::File.new(@new_resource.path) @scanner = Chef::ScanAccessControl.new(@new_resource, @current_resource) end describe "when the fs entity does not exist" do before do @new_resource.tap do |f| f.owner("root") f.group("root") f.mode('0755') end @scanner.set_all! end it "does not set any fields on the current resource" do expect(@current_resource.owner).to be_nil expect(@current_resource.group).to be_nil expect(@current_resource.mode).to be_nil end end describe "when the fs entity exists" do before do @stat = double("File::Stat for #{@new_resource.path}", :uid => 0, :gid => 0, :mode => 00100644) expect(File).to receive(:realpath).with(@new_resource.path).and_return(@real_file) expect(File).to receive(:stat).with(@real_file).and_return(@stat) expect(File).to receive(:exist?).with(@new_resource.path).and_return(true) end describe "when new_resource does not specify mode, user or group" do # these tests are necessary for minitest-chef-handler to use as an API, see CHEF-3235 before do @scanner.set_all! end it "sets the mode of the current resource to the current mode as a String" do expect(@current_resource.mode).to eq("0644") end context "on unix", :unix_only do it "sets the group of the current resource to the current group as a String" do expect(@current_resource.group).to eq(Etc.getgrgid(0).name) end it "sets the owner of the current resource to the current owner as a String" do expect(@current_resource.user).to eq("root") end end context "on windows", :windows_only do it "sets the group of the current resource to the current group as a String" do expect(@current_resource.group).to eq(0) end it "sets the owner of the current resource to the current owner as a String" do expect(@current_resource.user).to eq(0) end end end describe "when new_resource specifies the mode with a string" do before do @new_resource.mode("0755") @scanner.set_all! end it "sets the mode of the current resource to the file's current mode as a string" do expect(@current_resource.mode).to eq("0644") end end describe "when new_resource specified the mode with an integer" do before do @new_resource.mode(00755) @scanner.set_all! end it "sets the mode of the current resource to the current mode as a String" do expect(@current_resource.mode).to eq("0644") end end describe "when new_resource specifies the user with a UID" do before do @new_resource.user(0) @scanner.set_all! end it "sets the owner of current_resource to the UID of the current owner" do expect(@current_resource.user).to eq(0) end end describe "when new_resource specifies the user with a username" do before do @new_resource.user("root") end it "sets the owner of current_resource to the username of the current owner" do @root_passwd = double("Struct::Passwd for uid 0", :name => "root") expect(Etc).to receive(:getpwuid).with(0).and_return(@root_passwd) @scanner.set_all! expect(@current_resource.user).to eq("root") end describe "and there is no passwd entry for the user" do it "sets the owner of the current_resource to the UID" do expect(Etc).to receive(:getpwuid).with(0).and_raise(ArgumentError) @scanner.set_all! expect(@current_resource.user).to eq(0) end end end describe "when new_resource specifies the group with a GID" do before do @new_resource.group(0) @scanner.set_all! end it "sets the group of the current_resource to the gid of the current owner" do expect(@current_resource.group).to eq(0) end end describe "when new_resource specifies the group with a group name" do before do @new_resource.group("wheel") end it "sets the group of the current resource to the group name" do @group_entry = double("Struct::Group for wheel", :name => "wheel") expect(Etc).to receive(:getgrgid).with(0).and_return(@group_entry) @scanner.set_all! expect(@current_resource.group).to eq("wheel") end describe "and there is no group entry for the group" do it "sets the current_resource's group to the GID" do expect(Etc).to receive(:getgrgid).with(0).and_raise(ArgumentError) @scanner.set_all! expect(@current_resource.group).to eq(0) end end end end end chef-12.3.0/spec/unit/run_list/0000755000004100000410000000000012520074675016312 5ustar www-datawww-datachef-12.3.0/spec/unit/run_list/run_list_item_spec.rb0000644000004100000410000001041212520074675022524 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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::RunList::RunListItem do describe "when creating from a Hash" do it "raises an exception when the hash doesn't have a :type key" do expect {Chef::RunList::RunListItem.new(:name => "tatft")}.to raise_error(ArgumentError) end it "raises an exception when the hash doesn't have an :name key" do expect {Chef::RunList::RunListItem.new(:type => 'R') }.to raise_error(ArgumentError) end it "sets the name and type as given in the hash" do item = Chef::RunList::RunListItem.new(:type => 'fuuu', :name => 'uuuu') expect(item.to_s).to eq('fuuu[uuuu]') end end describe "when creating an item from a string" do it "parses a qualified recipe" do item = Chef::RunList::RunListItem.new("recipe[rage]") expect(item).to be_a_recipe expect(item).not_to be_a_role expect(item.to_s).to eq('recipe[rage]') expect(item.name).to eq('rage') end it "parses a qualified recipe with a version" do item = Chef::RunList::RunListItem.new("recipe[rage@0.1.0]") expect(item).to be_a_recipe expect(item).not_to be_a_role expect(item.to_s).to eq('recipe[rage@0.1.0]') expect(item.name).to eq('rage') expect(item.version).to eq('0.1.0') end it "parses a qualified role" do item = Chef::RunList::RunListItem.new("role[fist]") expect(item).to be_a_role expect(item).not_to be_a_recipe expect(item.to_s).to eq('role[fist]') expect(item.name).to eq('fist') end it "parses an unqualified recipe" do item = Chef::RunList::RunListItem.new("lobster") expect(item).to be_a_recipe expect(item).not_to be_a_role expect(item.to_s).to eq('recipe[lobster]') expect(item.name).to eq('lobster') end it "raises an exception when the string has typo on the type part" do expect {Chef::RunList::RunListItem.new("Recipe[lobster]") }.to raise_error(ArgumentError) end it "raises an exception when the string has extra space between the type and the name" do expect {Chef::RunList::RunListItem.new("recipe [lobster]") }.to raise_error(ArgumentError) end it "raises an exception when the string does not close the bracket" do expect {Chef::RunList::RunListItem.new("recipe[lobster") }.to raise_error(ArgumentError) end end describe "comparing to other run list items" do it "is equal to another run list item that has the same name and type" do item1 = Chef::RunList::RunListItem.new('recipe[lrf]') item2 = Chef::RunList::RunListItem.new('recipe[lrf]') expect(item1).to eq(item2) end it "is not equal to another run list item with the same name and different type" do item1 = Chef::RunList::RunListItem.new('recipe[lrf]') item2 = Chef::RunList::RunListItem.new('role[lrf]') expect(item1).not_to eq(item2) end it "is not equal to another run list item with the same type and different name" do item1 = Chef::RunList::RunListItem.new('recipe[lrf]') item2 = Chef::RunList::RunListItem.new('recipe[lobsterragefist]') expect(item1).not_to eq(item2) end it "is not equal to another run list item with the same name and type but different version" do item1 = Chef::RunList::RunListItem.new('recipe[lrf,0.1.0]') item2 = Chef::RunList::RunListItem.new('recipe[lrf,0.2.0]') expect(item1).not_to eq(item2) end end describe "comparing to strings" do it "is equal to a string if that string matches its to_s representation" do expect(Chef::RunList::RunListItem.new('recipe[lrf]')).to eq('recipe[lrf]') end end end chef-12.3.0/spec/unit/run_list/run_list_expansion_spec.rb0000644000004100000410000001005412520074675023574 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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::RunList::RunListExpansion do before do @run_list = Chef::RunList.new @run_list << 'recipe[lobster]' << 'role[rage]' << 'recipe[fist]' @expansion = Chef::RunList::RunListExpansion.new("_default", @run_list.run_list_items) end describe "before expanding the run list" do it "has an array of run list items" do expect(@expansion.run_list_items).to eq(@run_list.run_list_items) end it "has default_attrs" do expect(@expansion.default_attrs).to eq(Mash.new) end it "has override attrs" do expect(@expansion.override_attrs).to eq(Mash.new) end it "it has an empty list of recipes" do expect(@expansion.recipes.size).to eq(0) end it "has not applied its roles" do expect(@expansion.applied_role?('rage')).to be_falsey end end describe "after applying a role with environment-specific run lists" do before do @rage_role = Chef::Role.new.tap do |r| r.name("rage") r.env_run_lists('_default' => [], "prod" => ["recipe[prod-only]"]) end @expansion = Chef::RunList::RunListExpansion.new("prod", @run_list.run_list_items) expect(@expansion).to receive(:fetch_role).and_return(@rage_role) @expansion.expand end it "has the correct list of recipes for the given environment" do expect(@expansion.recipes).to eq(["lobster", "prod-only", "fist"]) end end describe "after applying a role" do before do allow(@expansion).to receive(:fetch_role).and_return(Chef::Role.new) @expansion.inflate_role('rage', "role[base]") end it "tracks the applied role" do expect(@expansion.applied_role?('rage')).to be_truthy end it "does not inflate the role again" do expect(@expansion.inflate_role('rage', "role[base]")).to be_falsey end end describe "after expanding a run list" do before do @first_role = Chef::Role.new @first_role.run_list('role[mollusk]') @first_role.default_attributes({'foo' => 'bar'}) @first_role.override_attributes({'baz' => 'qux'}) @second_role = Chef::Role.new @second_role.run_list('recipe[crabrevenge]') @second_role.default_attributes({'foo' => 'boo'}) @second_role.override_attributes({'baz' => 'bux'}) allow(@expansion).to receive(:fetch_role).and_return(@first_role, @second_role) @expansion.expand end it "has the ordered list of recipes" do expect(@expansion.recipes).to eq(['lobster', 'crabrevenge', 'fist']) end it "has the merged attributes from the roles with outer roles overriding inner" do expect(@expansion.default_attrs).to eq({'foo' => 'bar'}) expect(@expansion.override_attrs).to eq({'baz' => 'qux'}) end it "has the list of all roles applied" do # this is the correct order, but 1.8 hash order is not stable expect(@expansion.roles).to match_array(['rage', 'mollusk']) end end describe "after expanding a run list with a non existent role" do before do allow(@expansion).to receive(:fetch_role) { @expansion.role_not_found('crabrevenge', "role[base]") } @expansion.expand end it "is invalid" do expect(@expansion).to be_invalid expect(@expansion.errors?).to be_truthy # aliases end it "has a list of invalid role names" do expect(@expansion.errors).to include('crabrevenge') end end end chef-12.3.0/spec/unit/run_list/versioned_recipe_list_spec.rb0000644000004100000410000001006212520074675024230 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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::RunList::VersionedRecipeList do describe "initialize" do it "should create an empty array" do l = Chef::RunList::VersionedRecipeList.new expect(l).to eq([]) end end describe "add_recipe" do before(:each) do @list = Chef::RunList::VersionedRecipeList.new @list << "apt" @list << "god" @list << "apache2" end it "should append the recipe to the end of the list" do @list.add_recipe "rails" expect(@list).to eq(["apt", "god", "apache2", "rails"]) end it "should not duplicate entries" do @list.add_recipe "apt" expect(@list).to eq(["apt", "god", "apache2"]) end it "should allow you to specify a version" do @list.add_recipe "rails", "1.0.0" expect(@list).to eq(["apt", "god", "apache2", "rails"]) expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"}) end it "should allow you to specify a version for a recipe that already exists" do @list.add_recipe "apt", "1.2.3" expect(@list).to eq(["apt", "god", "apache2"]) expect(@list.with_versions).to include({:name => "apt", :version => "1.2.3"}) end it "should allow you to specify the same version of a recipe twice" do @list.add_recipe "rails", "1.0.0" @list.add_recipe "rails", "1.0.0" expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"}) end it "should allow you to spcify no version, even when a version already exists" do @list.add_recipe "rails", "1.0.0" @list.add_recipe "rails" expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"}) end it "should not allow multiple versions of the same recipe" do @list.add_recipe "rails", "1.0.0" expect {@list.add_recipe "rails", "0.1.0"}.to raise_error Chef::Exceptions::CookbookVersionConflict end end describe "with_versions" do before(:each) do @recipes = [ {:name => "apt", :version => "1.0.0"}, {:name => "god", :version => nil}, {:name => "apache2", :version => "0.0.1"} ] @list = Chef::RunList::VersionedRecipeList.new @recipes.each {|i| @list.add_recipe i[:name], i[:version]} end it "should return an array of hashes with :name and :version" do expect(@list.with_versions).to eq(@recipes) end it "should retain the same order as the version-less list" do with_versions = @list.with_versions @list.each_with_index do |item, index| expect(with_versions[index][:name]).to eq(item) end end end describe "with_version_constraints" do before(:each) do @recipes = [ {:name => "apt", :version => "~> 1.2.0"}, {:name => "god", :version => nil}, {:name => "apache2", :version => "0.0.1"} ] @list = Chef::RunList::VersionedRecipeList.new @recipes.each {|i| @list.add_recipe i[:name], i[:version]} @constraints = @recipes.map do |x| { :name => x[:name], :version_constraint => Chef::VersionConstraint.new(x[:version]) } end end it "should return an array of hashes with :name and :version_constraint" do @list.with_version_constraints.each do |x| expect(x).to have_key :name expect(x[:version_constraint]).not_to be nil end end end end chef-12.3.0/spec/unit/version_constraint/0000755000004100000410000000000012520074675020404 5ustar www-datawww-datachef-12.3.0/spec/unit/version_constraint/platform_spec.rb0000644000004100000410000000272712520074675023577 0ustar www-datawww-data# Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/version_constraint/platform' describe Chef::VersionConstraint::Platform do it "is a subclass of Chef::VersionConstraint" do v = Chef::VersionConstraint::Platform.new expect(v).to be_an_instance_of(Chef::VersionConstraint::Platform) expect(v).to be_a_kind_of(Chef::VersionConstraint) end it "should work with Chef::Version::Platform classes" do vc = Chef::VersionConstraint::Platform.new("1.0") expect(vc.version).to be_an_instance_of(Chef::Version::Platform) end describe "include?" do it "pessimistic ~> x" do vc = Chef::VersionConstraint::Platform.new "~> 1" expect(vc).to include "1.3.3" expect(vc).to include "1.4" expect(vc).not_to include "2.2" expect(vc).not_to include "0.3.0" end end end chef-12.3.0/spec/unit/json_compat_spec.rb0000644000004100000410000001162012520074675020326 0ustar www-datawww-data# # Author:: Juanje Ojeda () # 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 File.expand_path('../../spec_helper', __FILE__) require 'chef/json_compat' describe Chef::JSONCompat do describe "#from_json with JSON containing an existing class" do let(:json) { '{"json_class": "Chef::Role"}' } it "returns an instance of the class instead of a Hash" do expect(Chef::JSONCompat.from_json(json).class).to eq Chef::Role end end describe "#from_json with JSON containing comments" do let(:json) { %Q{{\n/* comment */\n// comment 2\n"json_class": "Chef::Role"}} } it "returns an instance of the class instead of a Hash" do expect(Chef::JSONCompat.from_json(json).class).to eq Chef::Role end end describe "#parse with JSON containing comments" do let(:json) { %Q{{\n/* comment */\n// comment 2\n"json_class": "Chef::Role"}} } it "returns a Hash" do expect(Chef::JSONCompat.parse(json).class).to eq Hash end end describe 'with JSON containing "Chef::Sandbox" as a json_class value' do require 'chef/sandbox' # Only needed for this test let(:json) { '{"json_class": "Chef::Sandbox", "arbitrary": "data"}' } it "returns a Hash, because Chef::Sandbox is a dummy class" do expect(Chef::JSONCompat.from_json(json)).to eq({"json_class" => "Chef::Sandbox", "arbitrary" => "data"}) end end describe "when pretty printing an object that defines #to_json" do class Foo def to_json(*a) Chef::JSONCompat.to_json({'foo' => 1234, 'bar' => {'baz' => 5678}}, *a) end end it "should work" do f = Foo.new expect(Chef::JSONCompat.to_json_pretty(f)).to eql("{\n \"foo\": 1234,\n \"bar\": {\n \"baz\": 5678\n }\n}\n") end include_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { Foo.new } end end # On FreeBSD 10.1 i386 rspec fails with a SystemStackError loading the expect line with more that 252 entries # https://github.com/chef/chef/issues/3101 describe "with the file with 252 or less nested entries" do let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'nested.json')) } let(:hash) { Chef::JSONCompat.from_json(json) } describe "when the 252 json file is loaded" do it "should create a Hash from the file" do expect(hash).to be_kind_of(Hash) end it "should has 'test' as a 252 nested value" do expect(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']).to eq('test') end end end it "should define .to_json on all classes" do class SomeClass; end expect(SomeClass.new.respond_to?(:to_json)).to eq(true) end end chef-12.3.0/spec/unit/daemon_spec.rb0000644000004100000410000001265312520074675017264 0ustar www-datawww-data# # Author:: AJ Christensen () # 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' require 'ostruct' describe Chef::Daemon do before do if windows? mock_struct = #Struct::Passwd.new(nil, nil, 111, 111) mock_struct = OpenStruct.new(:uid => 2342, :gid => 2342) allow(Etc).to receive(:getpwnam).and_return mock_struct allow(Etc).to receive(:getgrnam).and_return mock_struct # mock unimplemented methods allow(Process).to receive(:initgroups).and_return nil allow(Process::GID).to receive(:change_privilege).and_return 11 allow(Process::UID).to receive(:change_privilege).and_return 11 end end describe ".pid_file" do describe "when the pid_file option has been set" do before do Chef::Config[:pid_file] = "/var/run/chef/chef-client.pid" end it "should return the supplied value" do expect(Chef::Daemon.pid_file).to eql("/var/run/chef/chef-client.pid") end end describe "without the pid_file option set" do before do Chef::Daemon.name = "chef-client" end it "should return a valued based on @name" do expect(Chef::Daemon.pid_file).to eql("/tmp/chef-client.pid") end end end describe ".pid_from_file" do before do Chef::Config[:pid_file] = "/var/run/chef/chef-client.pid" end it "should suck the pid out of pid_file" do expect(File).to receive(:read).with("/var/run/chef/chef-client.pid").and_return("1337") Chef::Daemon.pid_from_file end end describe ".change_privilege" do before do allow(Chef::Application).to receive(:fatal!).and_return(true) Chef::Config[:user] = 'aj' allow(Dir).to receive(:chdir) end it "changes the working directory to root" do expect(Dir).to receive(:chdir).with("/").and_return(0) Chef::Daemon.change_privilege end describe "when the user and group options are supplied" do before do Chef::Config[:group] = 'staff' end it "should log an appropriate info message" do expect(Chef::Log).to receive(:info).with("About to change privilege to aj:staff") Chef::Daemon.change_privilege end it "should call _change_privilege with the user and group" do expect(Chef::Daemon).to receive(:_change_privilege).with("aj", "staff") Chef::Daemon.change_privilege end end describe "when just the user option is supplied" do it "should log an appropriate info message" do expect(Chef::Log).to receive(:info).with("About to change privilege to aj") Chef::Daemon.change_privilege end it "should call _change_privilege with just the user" do expect(Chef::Daemon).to receive(:_change_privilege).with("aj") Chef::Daemon.change_privilege end end end describe "._change_privilege" do before do allow(Process).to receive(:euid).and_return(0) allow(Process).to receive(:egid).and_return(0) allow(Process::UID).to receive(:change_privilege).and_return(nil) allow(Process::GID).to receive(:change_privilege).and_return(nil) @pw_user = double("Struct::Passwd", :uid => 501) @pw_group = double("Struct::Group", :gid => 20) allow(Process).to receive(:initgroups).and_return(true) allow(Etc).to receive(:getpwnam).and_return(@pw_user) allow(Etc).to receive(:getgrnam).and_return(@pw_group) end describe "with sufficient privileges" do before do allow(Process).to receive(:euid).and_return(0) allow(Process).to receive(:egid).and_return(0) end it "should initialize the supplemental group list" do expect(Process).to receive(:initgroups).with("aj", 20) Chef::Daemon._change_privilege("aj") end it "should attempt to change the process GID" do expect(Process::GID).to receive(:change_privilege).with(20).and_return(20) Chef::Daemon._change_privilege("aj") end it "should attempt to change the process UID" do expect(Process::UID).to receive(:change_privilege).with(501).and_return(501) Chef::Daemon._change_privilege("aj") end end describe "with insufficient privileges" do before do allow(Process).to receive(:euid).and_return(999) allow(Process).to receive(:egid).and_return(999) end it "should log an appropriate error message and fail miserably" do allow(Process).to receive(:initgroups).and_raise(Errno::EPERM) error = "Operation not permitted" if RUBY_PLATFORM.match("solaris2") || RUBY_PLATFORM.match("aix") error = "Not owner" end expect(Chef::Application).to receive(:fatal!).with("Permission denied when trying to change 999:999 to 501:20. #{error}") Chef::Daemon._change_privilege("aj") end end end end chef-12.3.0/spec/unit/version/0000755000004100000410000000000012520074675016140 5ustar www-datawww-datachef-12.3.0/spec/unit/version/platform_spec.rb0000644000004100000410000000364412520074675021332 0ustar www-datawww-data# Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/version/platform' describe Chef::Version::Platform do it "is a subclass of Chef::Version" do v = Chef::Version::Platform.new('1.1') expect(v).to be_an_instance_of(Chef::Version::Platform) expect(v).to be_a_kind_of(Chef::Version) end it "should transform 1 to 1.0.0" do expect(Chef::Version::Platform.new("1").to_s).to eq("1.0.0") end describe "when creating valid Versions" do good_versions = %w(1 1.2 1.2.3 1000.80.50000 0.300.25 001.02.00003 1.2-STABLE 10.0-BETA3 9.1-RELEASE-p3) good_versions.each do |v| it "should accept '#{v}'" do Chef::Version::Platform.new v end end end describe "when given bogus input" do bad_versions = ["1.2.3.4", "1.2.a4", "a", "1.2 3", "1.2 a", "1 2 3", "1-2-3", "1_2_3", "1.2_3", "1.2-3"] the_error = Chef::Exceptions::InvalidPlatformVersion bad_versions.each do |v| it "should raise #{the_error} when given '#{v}'" do expect { Chef::Version::Platform.new v }.to raise_error(the_error) end end end describe "<=>" do it "should equate versions 1 and 1.0.0" do expect(Chef::Version::Platform.new("1")).to eq(Chef::Version::Platform.new("1.0.0")) end end end chef-12.3.0/spec/unit/encrypted_data_bag_item/0000755000004100000410000000000012520074675021270 5ustar www-datawww-datachef-12.3.0/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb0000644000004100000410000000612012520074675025760 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2010-2014 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/encrypted_data_bag_item/check_encrypted' class CheckEncryptedTester include Chef::EncryptedDataBagItem::CheckEncrypted end describe Chef::EncryptedDataBagItem::CheckEncrypted do let(:tester) { CheckEncryptedTester.new } it "detects the item is not encrypted when the data is empty" do expect(tester.encrypted?({})).to eq(false) end it "detects the item is not encrypted when the data only contains an id" do expect(tester.encrypted?({id: "foo"})).to eq(false) end context "when the item is encrypted" do let(:default_secret) { "abc123SECRET" } let(:item_name) { "item_name" } let(:raw_data) {{ "id" => item_name, "greeting" => "hello", "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } } }} let(:version) { 1 } let(:encoded_data) do Chef::Config[:data_bag_encrypt_version] = version Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_data, default_secret) end it "does not detect encryption when the item version is unknown" do # It shouldn't be possible for someone to normally encrypt an item with an unknown version - they would have to # do something funky like encrypting it and then manually changing the version modified_encoded_data = encoded_data modified_encoded_data["greeting"]["version"] = 4 expect(tester.encrypted?(modified_encoded_data)).to eq(false) end shared_examples_for "encryption detected" do it "detects encrypted data bag" do expect( encryptor ).to receive(:encryptor_keys).at_least(:once).and_call_original expect(tester.encrypted?(encoded_data)).to eq(true) end end context "when encryption version is 1" do include_examples "encryption detected" do let(:version) { 1 } let(:encryptor) { Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor } end end context "when encryption version is 2" do include_examples "encryption detected" do let(:version) { 2 } let(:encryptor) { Chef::EncryptedDataBagItem::Encryptor::Version2Encryptor } end end context "when encryption version is 3", :aes_256_gcm_only, :ruby_20_only do include_examples "encryption detected" do let(:version) { 3 } let(:encryptor) { Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor } end end end end chef-12.3.0/spec/unit/node/0000755000004100000410000000000012520074675015400 5ustar www-datawww-datachef-12.3.0/spec/unit/node/immutable_collections_spec.rb0000644000004100000410000001205712520074675023321 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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/node/immutable_collections" describe Chef::Node::ImmutableMash do before do @data_in = {:top => {:second_level => "some value"}, "top_level_2" => %w[array of values], :top_level_3 => [{:hash_array => 1, :hash_array_b => 2}], :top_level_4 => {:level2 => {:key => "value"}} } @immutable_mash = Chef::Node::ImmutableMash.new(@data_in) end it "element references like regular hash" do expect(@immutable_mash[:top][:second_level]).to eq("some value") end it "element references like a regular Mash" do expect(@immutable_mash[:top_level_2]).to eq(%w[array of values]) end it "converts Hash-like inputs into ImmutableMash's" do expect(@immutable_mash[:top]).to be_a(Chef::Node::ImmutableMash) end it "converts array inputs into ImmutableArray's" do expect(@immutable_mash[:top_level_2]).to be_a(Chef::Node::ImmutableArray) end it "converts arrays of hashes to ImmutableArray's of ImmutableMashes" do expect(@immutable_mash[:top_level_3].first).to be_a(Chef::Node::ImmutableMash) end it "converts nested hashes to ImmutableMashes" do expect(@immutable_mash[:top_level_4]).to be_a(Chef::Node::ImmutableMash) expect(@immutable_mash[:top_level_4][:level2]).to be_a(Chef::Node::ImmutableMash) end describe "to_hash" do before do @copy = @immutable_mash.to_hash end it "converts an immutable mash to a new mutable hash" do expect(@copy).to be_instance_of(Hash) end it "converts an immutable nested mash to a new mutable hash" do expect(@copy['top_level_4']['level2']).to be_instance_of(Hash) end it "converts an immutable nested array to a new mutable array" do expect(@copy['top_level_2']).to be_instance_of(Array) end it "should create a mash with the same content" do expect(@copy).to eq(@immutable_mash) end it 'should allow mutation' do expect { @copy['m'] = 'm' }.not_to raise_error end end [ :[]=, :clear, :default=, :default_proc=, :delete, :delete_if, :keep_if, :merge!, :update, :reject!, :replace, :select!, :shift ].each do |mutator| it "doesn't allow mutation via `#{mutator}'" do expect { @immutable_mash.send(mutator) }.to raise_error end end it "returns a mutable version of itself when duped" do mutable = @immutable_mash.dup mutable[:new_key] = :value expect(mutable[:new_key]).to eq(:value) end end describe Chef::Node::ImmutableArray do before do @immutable_array = Chef::Node::ImmutableArray.new(%w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ]) immutable_mash = Chef::Node::ImmutableMash.new({:m => 'm'}) @immutable_nested_array = Chef::Node::ImmutableArray.new(["level1",@immutable_array, immutable_mash]) end ## # Note: other behaviors, such as immutibilizing input data, are tested along # with ImmutableMash, above ### [ :<<, :[]=, :clear, :collect!, :compact!, :default=, :default_proc=, :delete, :delete_at, :delete_if, :fill, :flatten!, :insert, :keep_if, :map!, :merge!, :pop, :push, :update, :reject!, :reverse!, :replace, :select!, :shift, :slice!, :sort!, :sort_by!, :uniq!, :unshift ].each do |mutator| it "does not allow mutation via `#{mutator}" do expect { @immutable_array.send(mutator)}.to raise_error end end it "can be duped even if some elements can't" do @immutable_array.dup end it "returns a mutable version of itself when duped" do mutable = @immutable_array.dup mutable[0] = :value expect(mutable[0]).to eq(:value) end describe "to_a" do before do @copy = @immutable_nested_array.to_a end it "converts an immutable array to a new mutable array" do expect(@copy).to be_instance_of(Array) end it "converts an immutable nested array to a new mutable array" do expect(@copy[1]).to be_instance_of(Array) end it "converts an immutable nested mash to a new mutable hash" do expect(@copy[2]).to be_instance_of(Hash) end it "should create an array with the same content" do expect(@copy).to eq(@immutable_nested_array) end it 'should allow mutation' do expect { @copy << 'm' }.not_to raise_error end end end chef-12.3.0/spec/unit/node/attribute_spec.rb0000644000004100000410000011636412520074675020755 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen () # 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' require 'chef/node/attribute' describe Chef::Node::Attribute do before(:each) do @attribute_hash = {"dmi"=>{}, "command"=>{"ps"=>"ps -ef"}, "platform_version"=>"10.5.7", "platform"=>"mac_os_x", "ipaddress"=>"192.168.0.117", "network"=> {"default_interface"=>"en1", "interfaces"=> {"vmnet1"=> {"flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"1", "addresses"=> {"00:50:56:c0:00:01"=>{"family"=>"lladdr"}, "192.168.110.1"=> {"broadcast"=>"192.168.110.255", "netmask"=>"255.255.255.0", "family"=>"inet"}}, "mtu"=>"1500", "type"=>"vmnet", "arp"=>{"192.168.110.255"=>"ff:ff:ff:ff:ff:ff"}, "encapsulation"=>"Ethernet"}, "stf0"=> {"flags"=>[], "number"=>"0", "addresses"=>{}, "mtu"=>"1280", "type"=>"stf", "encapsulation"=>"6to4"}, "lo0"=> {"flags"=>["UP", "LOOPBACK", "RUNNING", "MULTICAST"], "number"=>"0", "addresses"=> {"::1"=>{"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6"}, "127.0.0.1"=>{"netmask"=>"255.0.0.0", "family"=>"inet"}, "fe80::1"=>{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6"}}, "mtu"=>"16384", "type"=>"lo", "encapsulation"=>"Loopback"}, "gif0"=> {"flags"=>["POINTOPOINT", "MULTICAST"], "number"=>"0", "addresses"=>{}, "mtu"=>"1280", "type"=>"gif", "encapsulation"=>"IPIP"}, "vmnet8"=> {"flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"8", "addresses"=> {"192.168.4.1"=> {"broadcast"=>"192.168.4.255", "netmask"=>"255.255.255.0", "family"=>"inet"}, "00:50:56:c0:00:08"=>{"family"=>"lladdr"}}, "mtu"=>"1500", "type"=>"vmnet", "arp"=>{"192.168.4.255"=>"ff:ff:ff:ff:ff:ff"}, "encapsulation"=>"Ethernet"}, "en0"=> {"status"=>"inactive", "flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"0", "addresses"=>{"00:23:32:b0:32:f2"=>{"family"=>"lladdr"}}, "mtu"=>"1500", "media"=> {"supported"=> {"autoselect"=>{"options"=>[]}, "none"=>{"options"=>[]}, "1000baseT"=> {"options"=>["full-duplex", "flow-control", "hw-loopback"]}, "10baseT/UTP"=> {"options"=> ["half-duplex", "full-duplex", "flow-control", "hw-loopback"]}, "100baseTX"=> {"options"=> ["half-duplex", "full-duplex", "flow-control", "hw-loopback"]}}, "selected"=>{"autoselect"=>{"options"=>[]}}}, "type"=>"en", "encapsulation"=>"Ethernet"}, "en1"=> {"status"=>"active", "flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"1", "addresses"=> {"fe80::223:6cff:fe7f:676c"=> {"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6"}, "00:23:6c:7f:67:6c"=>{"family"=>"lladdr"}, "192.168.0.117"=> {"broadcast"=>"192.168.0.255", "netmask"=>"255.255.255.0", "family"=>"inet"}}, "mtu"=>"1500", "media"=> {"supported"=>{"autoselect"=>{"options"=>[]}}, "selected"=>{"autoselect"=>{"options"=>[]}}}, "type"=>"en", "arp"=> {"192.168.0.72"=>"0:f:ea:39:fa:d5", "192.168.0.1"=>"0:1c:fb:fc:6f:20", "192.168.0.255"=>"ff:ff:ff:ff:ff:ff", "192.168.0.3"=>"0:1f:33:ea:26:9b", "192.168.0.77"=>"0:23:12:70:f8:cf", "192.168.0.152"=>"0:26:8:7d:2:4c"}, "encapsulation"=>"Ethernet"}, "en2"=> {"status"=>"active", "flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"2", "addresses"=> {"169.254.206.152"=> {"broadcast"=>"169.254.255.255", "netmask"=>"255.255.0.0", "family"=>"inet"}, "00:1c:42:00:00:01"=>{"family"=>"lladdr"}, "fe80::21c:42ff:fe00:1"=> {"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6"}}, "mtu"=>"1500", "media"=> {"supported"=>{"autoselect"=>{"options"=>[]}}, "selected"=>{"autoselect"=>{"options"=>[]}}}, "type"=>"en", "encapsulation"=>"Ethernet"}, "fw0"=> {"status"=>"inactive", "flags"=>["BROADCAST", "SIMPLEX", "MULTICAST"], "number"=>"0", "addresses"=>{"00:23:32:ff:fe:b0:32:f2"=>{"family"=>"lladdr"}}, "mtu"=>"4078", "media"=> {"supported"=>{"autoselect"=>{"options"=>["full-duplex"]}}, "selected"=>{"autoselect"=>{"options"=>["full-duplex"]}}}, "type"=>"fw", "encapsulation"=>"1394"}, "en3"=> {"status"=>"active", "flags"=> ["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"3", "addresses"=> {"169.254.206.152"=> {"broadcast"=>"169.254.255.255", "netmask"=>"255.255.0.0", "family"=>"inet"}, "00:1c:42:00:00:00"=>{"family"=>"lladdr"}, "fe80::21c:42ff:fe00:0"=> {"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6"}}, "mtu"=>"1500", "media"=> {"supported"=>{"autoselect"=>{"options"=>[]}}, "selected"=>{"autoselect"=>{"options"=>[]}}}, "type"=>"en", "encapsulation"=>"Ethernet"}}}, "fqdn"=>"latte.local", "ohai_time"=>1249065590.90391, "domain"=>"local", "os"=>"darwin", "platform_build"=>"9J61", "os_version"=>"9.7.0", "hostname"=>"latte", "macaddress"=>"00:23:6c:7f:67:6c", "music" => { "jimmy_eat_world" => "nice", "apophis" => false } } @default_hash = { "domain" => "opscode.com", "hot" => { "day" => "saturday" }, "music" => { "jimmy_eat_world" => "is fun!", "mastodon" => "rocks", "mars_volta" => "is loud and nutty", "deeper" => { "gates_of_ishtar" => nil }, "this" => {"apparatus" => {"must" => "be unearthed"}} } } @override_hash = { "macaddress" => "00:00:00:00:00:00", "hot" => { "day" => "sunday" }, "fire" => "still burn", "music" => { "mars_volta" => "cicatriz" } } @automatic_hash = {"week" => "friday"} @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash) end describe "initialize" do it "should return a Chef::Node::Attribute" do expect(@attributes).to be_a_kind_of(Chef::Node::Attribute) end it "should take an Automatioc, Normal, Default and Override hash" do expect { Chef::Node::Attribute.new({}, {}, {}, {}) }.not_to raise_error end [ :normal, :default, :override, :automatic ].each do |accessor| it "should set #{accessor}" do na = Chef::Node::Attribute.new({ :normal => true }, { :default => true }, { :override => true }, { :automatic => true }) expect(na.send(accessor)).to eq({ accessor.to_s => true }) end end it "should be enumerable" do expect(@attributes).to be_is_a(Enumerable) end end describe "when printing attribute components" do it "does not cause a type error" do # See CHEF-3799; IO#puts implicitly calls #to_ary on its argument. This # is expected to raise a NoMethodError or return an Array. `to_ary` is # the "strict" conversion method that should only be implemented by # things that are truly Array-like, so NoMethodError is the right choice. # (cf. there is no Hash#to_ary). expect { @attributes.default.to_ary }.to raise_error(NoMethodError) end end describe "when debugging attributes" do before do @attributes.default[:foo][:bar] = "default" @attributes.env_default[:foo][:bar] = "env_default" @attributes.role_default[:foo][:bar] = "role_default" @attributes.force_default[:foo][:bar] = "force_default" @attributes.normal[:foo][:bar] = "normal" @attributes.override[:foo][:bar] = "override" @attributes.role_override[:foo][:bar] = "role_override" @attributes.env_override[:foo][:bar] = "env_override" @attributes.force_override[:foo][:bar] = "force_override" @attributes.automatic[:foo][:bar] = "automatic" end it "gives the value at each level of precedence for a path spec" do expected = [["set_unless_enabled?", false], ["default", "default"], ["env_default", "env_default"], ["role_default", "role_default"], ["force_default", "force_default"], ["normal", "normal"], ["override", "override"], ["role_override", "role_override"], ["env_override", "env_override"], ["force_override", "force_override"], ["automatic", "automatic"] ] expect(@attributes.debug_value(:foo, :bar)).to eq(expected) end end describe "when fetching values based on precedence" do before do @attributes.default["default"] = "cookbook default" @attributes.override["override"] = "cookbook override" end it "prefers 'forced default' over any other default" do @attributes.force_default["default"] = "force default" @attributes.role_default["default"] = "role default" @attributes.env_default["default"] = "environment default" expect(@attributes["default"]).to eq("force default") end it "prefers role_default over environment or cookbook default" do @attributes.role_default["default"] = "role default" @attributes.env_default["default"] = "environment default" expect(@attributes["default"]).to eq("role default") end it "prefers environment default over cookbook default" do @attributes.env_default["default"] = "environment default" expect(@attributes["default"]).to eq("environment default") end it "returns the cookbook default when no other default values are present" do expect(@attributes["default"]).to eq("cookbook default") end it "prefers 'forced overrides' over role or cookbook overrides" do @attributes.force_override["override"] = "force override" @attributes.env_override["override"] = "environment override" @attributes.role_override["override"] = "role override" expect(@attributes["override"]).to eq("force override") end it "prefers environment overrides over role or cookbook overrides" do @attributes.env_override["override"] = "environment override" @attributes.role_override["override"] = "role override" expect(@attributes["override"]).to eq("environment override") end it "prefers role overrides over cookbook overrides" do @attributes.role_override["override"] = "role override" expect(@attributes["override"]).to eq("role override") end it "returns cookbook overrides when no other overrides are present" do expect(@attributes["override"]).to eq("cookbook override") end it "merges arrays within the default precedence" do @attributes.role_default["array"] = %w{role} @attributes.env_default["array"] = %w{env} expect(@attributes["array"]).to eq(%w{env role}) end it "merges arrays within the override precedence" do @attributes.role_override["array"] = %w{role} @attributes.env_override["array"] = %w{env} expect(@attributes["array"]).to eq(%w{role env}) end it "does not merge arrays between default and normal" do @attributes.role_default["array"] = %w{role} @attributes.normal["array"] = %w{normal} expect(@attributes["array"]).to eq(%w{normal}) end it "does not merge arrays between normal and override" do @attributes.normal["array"] = %w{normal} @attributes.role_override["array"] = %w{role} expect(@attributes["array"]).to eq(%w{role}) end it "merges nested hashes between precedence levels" do @attributes = Chef::Node::Attribute.new({}, {}, {}, {}) @attributes.env_default = {"a" => {"b" => {"default" => "default"}}} @attributes.normal = {"a" => {"b" => {"normal" => "normal"}}} @attributes.override = {"a" => {"override" => "role"}} @attributes.automatic = {"a" => {"automatic" => "auto"}} expect(@attributes["a"]).to eq({"b"=>{"default"=>"default", "normal"=>"normal"}, "override"=>"role", "automatic"=>"auto"}) end end describe "when reading combined default or override values" do before do @attributes.default["cd"] = "cookbook default" @attributes.role_default["rd"] = "role default" @attributes.env_default["ed"] = "env default" @attributes.default!["fd"] = "force default" @attributes.override["co"] = "cookbook override" @attributes.role_override["ro"] = "role override" @attributes.env_override["eo"] = "env override" @attributes.override!["fo"] = "force override" end it "merges all types of overrides into a combined override" do expect(@attributes.combined_override["co"]).to eq("cookbook override") expect(@attributes.combined_override["ro"]).to eq("role override") expect(@attributes.combined_override["eo"]).to eq("env override") expect(@attributes.combined_override["fo"]).to eq("force override") end it "merges all types of defaults into a combined default" do expect(@attributes.combined_default["cd"]).to eq("cookbook default") expect(@attributes.combined_default["rd"]).to eq("role default") expect(@attributes.combined_default["ed"]).to eq("env default") expect(@attributes.combined_default["fd"]).to eq("force default") end end describe "[]" do it "should return override data if it exists" do expect(@attributes["macaddress"]).to eq("00:00:00:00:00:00") end it "should return attribute data if it is not overridden" do expect(@attributes["platform"]).to eq("mac_os_x") end it "should return data that doesn't have corresponding keys in every hash" do expect(@attributes["command"]["ps"]).to eq("ps -ef") end it "should return default data if it is not overriden or in attribute data" do expect(@attributes["music"]["mastodon"]).to eq("rocks") end it "should prefer the override data over an available default" do expect(@attributes["music"]["mars_volta"]).to eq("cicatriz") end it "should prefer the attribute data over an available default" do expect(@attributes["music"]["jimmy_eat_world"]).to eq("nice") end it "should prefer override data over default data if there is no attribute data" do expect(@attributes["hot"]["day"]).to eq("sunday") end it "should return the merged hash if all three have values" do result = @attributes["music"] expect(result["mars_volta"]).to eq("cicatriz") expect(result["jimmy_eat_world"]).to eq("nice") expect(result["mastodon"]).to eq("rocks") end end describe "[]=" do it "should error out when the type of attribute to set has not been specified" do @attributes.normal["the_ghost"] = { } expect { @attributes["the_ghost"]["exterminate"] = false }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end it "should let you set an attribute value when another hash has an intermediate value" do @attributes.normal["the_ghost"] = { "exterminate" => "the future" } expect { @attributes.normal["the_ghost"]["eviscerate"]["tomorrow"] = false }.not_to raise_error end it "should set the attribute value" do @attributes.normal["longboard"] = "surfing" expect(@attributes.normal["longboard"]).to eq("surfing") expect(@attributes.normal["longboard"]).to eq("surfing") end it "should set deeply nested attribute values when a precedence level is specified" do @attributes.normal["deftones"]["hunters"]["nap"] = "surfing" expect(@attributes.normal["deftones"]["hunters"]["nap"]).to eq("surfing") end it "should die if you try and do nested attributes that do not exist without read vivification" do expect { @attributes["foo"]["bar"] = :baz }.to raise_error end it "should let you set attributes manually without vivification" do @attributes.normal["foo"] = Mash.new @attributes.normal["foo"]["bar"] = :baz expect(@attributes.normal["foo"]["bar"]).to eq(:baz) end it "should optionally skip setting the value if one already exists" do @attributes.set_unless_value_present = true @attributes.normal["hostname"] = "bar" expect(@attributes["hostname"]).to eq("latte") end it "does not support ||= when setting" do # This is a limitation of auto-vivification. # Users who need this behavior can use set_unless and friends @attributes.normal["foo"] = Mash.new @attributes.normal["foo"]["bar"] ||= "stop the world" expect(@attributes.normal["foo"]["bar"]).to eq({}) end end describe "to_hash" do it "should convert to a hash" do expect(@attributes.to_hash.class).to eq(Hash) end it "should convert to a hash based on current state" do hash = @attributes["hot"].to_hash expect(hash.class).to eq(Hash) expect(hash["day"]).to eq("sunday") end it "should create a deep copy of the node attribute" do @attributes.default['foo']['bar']['baz'] = 'fizz' hash = @attributes['foo'].to_hash expect(hash).to eql({"bar"=>{"baz"=>"fizz"}}) hash['bar']['baz'] = 'buzz' expect(hash).to eql({"bar"=>{"baz"=>"buzz"}}) expect(@attributes.default['foo']).to eql({"bar"=>{"baz"=>"fizz"}}) end it "should create a deep copy of arrays in the node attribute" do @attributes.default['foo']['bar'] = ['fizz'] hash = @attributes['foo'].to_hash expect(hash).to eql({"bar"=>[ 'fizz' ]}) hash['bar'].push('buzz') expect(hash).to eql({"bar"=>[ 'fizz', 'buzz' ]}) expect(@attributes.default['foo']).to eql({"bar"=>[ 'fizz' ]}) end it "mutating strings should not mutate the attributes" do pending "this is a bug that should be fixed" @attributes.default['foo']['bar']['baz'] = 'fizz' hash = @attributes['foo'].to_hash expect(hash).to eql({"bar"=>{"baz"=>"fizz"}}) hash['bar']['baz'] << 'buzz' expect(hash).to eql({"bar"=>{"baz"=>"fizzbuzz"}}) expect(@attributes.default['foo']).to eql({"bar"=>{"baz"=>"fizz"}}) end end describe "dup" do it "array can be duped even if some elements can't" do @attributes.default[:foo] = %w[foo bar baz] + Array(1..3) + [nil, true, false, [ "el", 0, nil ] ] @attributes.default[:foo].dup end end describe "has_key?" do it "should return true if an attribute exists" do expect(@attributes.has_key?("music")).to eq(true) end it "should return false if an attribute does not exist" do expect(@attributes.has_key?("ninja")).to eq(false) end it "should return false if an attribute does not exist using dot notation" do expect(@attributes.has_key?("does_not_exist_at_all")).to eq(false) end it "should return true if an attribute exists but is set to nil using dot notation" do expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true) end it "should return true if an attribute exists but is set to false" do @attributes.has_key?("music") expect(@attributes["music"].has_key?("apophis")).to eq(true) end it "does not find keys above the current nesting level" do expect(@attributes["music"]["this"]["apparatus"]).not_to have_key("this") end it "does not find keys below the current nesting level" do expect(@attributes["music"]["this"]).not_to have_key("must") end [:include?, :key?, :member?].each do |method| it "should alias the method #{method} to itself" do expect(@attributes).to respond_to(method) end it "#{method} should behave like has_key?" do expect(@attributes.send(method, "music")).to eq(true) end end end describe "attribute?" do it "should return true if an attribute exists" do expect(@attributes.attribute?("music")).to eq(true) end it "should return false if an attribute does not exist" do expect(@attributes.attribute?("ninja")).to eq(false) end end describe "method_missing" do it "should behave like a [] lookup" do expect(@attributes.music.mastodon).to eq("rocks") end it "should allow the last method to set a value if it has an = sign on the end" do @attributes.normal.music.mastodon = [ "dream", "still", "shining" ] expect(@attributes.normal.music.mastodon).to eq([ "dream", "still", "shining" ]) end end describe "keys" do before(:each) do @attributes = Chef::Node::Attribute.new( { "one" => { "two" => "three" }, "hut" => { "two" => "three" }, "place" => { } }, { "one" => { "four" => "five" }, "snakes" => "on a plane" }, { "one" => { "six" => "seven" }, "snack" => "cookies" }, {} ) end it "should yield each top level key" do collect = Array.new @attributes.keys.each do |k| collect << k end expect(collect.include?("one")).to eq(true) expect(collect.include?("hut")).to eq(true) expect(collect.include?("snakes")).to eq(true) expect(collect.include?("snack")).to eq(true) expect(collect.include?("place")).to eq(true) expect(collect.length).to eq(5) end it "should yield lower if we go deeper" do collect = Array.new @attributes.one.keys.each do |k| collect << k end expect(collect.include?("two")).to eq(true) expect(collect.include?("four")).to eq(true) expect(collect.include?("six")).to eq(true) expect(collect.length).to eq(3) end it "should not raise an exception if one of the hashes has a nil value on a deep lookup" do expect { @attributes.place.keys { |k| } }.not_to raise_error end end describe "each" do before(:each) do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should yield each top level key and value, post merge rules" do collect = Hash.new @attributes.each do |k, v| collect[k] = v end expect(collect["one"]).to eq("six") expect(collect["hut"]).to eq("three") expect(collect["snakes"]).to eq("on a plane") expect(collect["snack"]).to eq("cookies") end it "should yield as a two-element array" do @attributes.each do |a| expect(a).to be_an_instance_of(Array) end end end describe "each_key" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to each_key" do expect(@attributes).to respond_to(:each_key) end it "should yield each top level key, post merge rules" do collect = Array.new @attributes.each_key do |k| collect << k end expect(collect).to include("one") expect(collect).to include("snack") expect(collect).to include("hut") expect(collect).to include("snakes") end end describe "each_pair" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to each_pair" do expect(@attributes).to respond_to(:each_pair) end it "should yield each top level key and value pair, post merge rules" do collect = Hash.new @attributes.each_pair do |k, v| collect[k] = v end expect(collect["one"]).to eq("six") expect(collect["hut"]).to eq("three") expect(collect["snakes"]).to eq("on a plane") expect(collect["snack"]).to eq("cookies") end end describe "each_value" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to each_value" do expect(@attributes).to respond_to(:each_value) end it "should yield each value, post merge rules" do collect = Array.new @attributes.each_value do |v| collect << v end expect(collect).to include("cookies") expect(collect).to include("three") expect(collect).to include("on a plane") end it "should yield four elements" do collect = Array.new @attributes.each_value do |v| collect << v end expect(collect.length).to eq(4) end end describe "empty?" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) @empty = Chef::Node::Attribute.new({}, {}, {}, {}) end it "should respond to empty?" do expect(@attributes).to respond_to(:empty?) end it "should return true when there are no keys" do expect(@empty.empty?).to eq(true) end it "should return false when there are keys" do expect(@attributes.empty?).to eq(false) end end describe "fetch" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to fetch" do expect(@attributes).to respond_to(:fetch) end describe "when the key exists" do it "should return the value of the key, post merge (same result as each)" do { "one" => "six", "hut" => "three", "snakes" => "on a plane", "snack" => "cookies" }.each do |k,v| expect(@attributes.fetch(k)).to eq(v) end end end describe "when the key does not exist" do describe "and no args are passed" do it "should raise an indexerror" do expect { @attributes.fetch("lololol") }.to raise_error(IndexError) end end describe "and a default arg is passed" do it "should return the value of the default arg" do expect(@attributes.fetch("lol", "blah")).to eq("blah") end end describe "and a block is passed" do it "should run the block and return its value" do expect(@attributes.fetch("lol") { |x| "#{x}, blah" }).to eq("lol, blah") end end end end describe "has_value?" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to has_value?" do expect(@attributes).to respond_to(:has_value?) end it "should return true if any key has the value supplied" do expect(@attributes.has_value?("cookies")).to eq(true) end it "should return false no key has the value supplied" do expect(@attributes.has_value?("lololol")).to eq(false) end it "should alias value?" do expect(@attributes).to respond_to(:value?) end end describe "index" do # Hash#index is deprecated and triggers warnings. def silence old_verbose = $VERBOSE $VERBOSE = nil yield ensure $VERBOSE = old_verbose end before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to index" do expect(@attributes).to respond_to(:index) end describe "when the value is indexed" do it "should return the index" do silence do expect(@attributes.index("six")).to eq("one") end end end describe "when the value is not indexed" do it "should return nil" do silence do expect(@attributes.index("lolol")).to eq(nil) end end end end describe "values" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to values" do expect(@attributes).to respond_to(:values) end it "should return an array of values" do expect(@attributes.values.length).to eq(4) end it "should match the values output from each" do expect(@attributes.values).to include("six") expect(@attributes.values).to include("cookies") expect(@attributes.values).to include("three") expect(@attributes.values).to include("on a plane") end end describe "select" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) end it "should respond to select" do expect(@attributes).to respond_to(:select) end if RUBY_VERSION >= "1.8.7" it "should not raise a LocalJumpError if no block is given" do expect { @attributes.select }.not_to raise_error end else it "should raise a LocalJumpError if no block is given" do expect{ @attributes.select }.to raise_error(LocalJumpError) end end it "should return an empty hash/array (ruby-version-dependent) for a block containing nil" do expect(@attributes.select { nil }).to eq({}.select { nil }) end # sorted for spec clarity it "should return a new array of k,v pairs for which the block returns true" do expect(@attributes.select { true }.sort).to eq( [ ["hut", "three"], ["one", "six"], ["snack", "cookies"], ["snakes", "on a plane"] ] ) end end describe "size" do before do @attributes = Chef::Node::Attribute.new( { "one" => "two", "hut" => "three", }, { "one" => "four", "snakes" => "on a plane" }, { "one" => "six", "snack" => "cookies" }, {} ) @empty = Chef::Node::Attribute.new({},{},{},{}) end it "should respond to size" do expect(@attributes).to respond_to(:size) end it "should alias length to size" do expect(@attributes).to respond_to(:length) end it "should return 0 for an empty attribute" do expect(@empty.size).to eq(0) end it "should return the number of pairs" do expect(@attributes.size).to eq(4) end end describe "kind_of?" do it "should falsely inform you that it is a Hash" do expect(@attributes).to be_a_kind_of(Hash) end it "should falsely inform you that it is a Mash" do expect(@attributes).to be_a_kind_of(Mash) end it "should inform you that it is a Chef::Node::Attribute" do expect(@attributes).to be_a_kind_of(Chef::Node::Attribute) end it "should inform you that it is anything else" do expect(@attributes).not_to be_a_kind_of(Chef::Node) end end describe "to_s" do it "should output simple attributes" do attributes = Chef::Node::Attribute.new(nil, nil, nil, nil) expect(attributes.to_s).to eq("{}") end it "should output merged attributes" do default_hash = { "a" => 1, "b" => 2 } override_hash = { "b" => 3, "c" => 4 } attributes = Chef::Node::Attribute.new(nil, default_hash, override_hash, nil) expect(attributes.to_s).to eq('{"a"=>1, "b"=>3, "c"=>4}') end end describe "inspect" do it "should be readable" do # NOTE: previous implementation hid the values, showing @automatic={...} # That is nice and compact, but hides a lot of info, which seems counter # to the point of calling #inspect... expect(@attributes.inspect).to match(/@automatic=\{.*\}/) expect(@attributes.inspect).to match(/@normal=\{.*\}/) end end describe "when not mutated" do it "does not reset the cache when dup'd [CHEF-3680]" do @attributes.default[:foo][:bar] = "set on original" subtree = @attributes[:foo] @attributes.default[:foo].dup[:bar] = "set on dup" expect(subtree[:bar]).to eq("set on original") end end describe "when setting a component attribute to a new value" do it "converts the input in to a VividMash tree (default)" do @attributes.default = {} @attributes.default.foo = "bar" expect(@attributes.merged_attributes[:foo]).to eq("bar") end it "converts the input in to a VividMash tree (normal)" do @attributes.normal = {} @attributes.normal.foo = "bar" expect(@attributes.merged_attributes[:foo]).to eq("bar") end it "converts the input in to a VividMash tree (override)" do @attributes.override = {} @attributes.override.foo = "bar" expect(@attributes.merged_attributes[:foo]).to eq("bar") end it "converts the input in to a VividMash tree (automatic)" do @attributes.automatic = {} @attributes.automatic.foo = "bar" expect(@attributes.merged_attributes[:foo]).to eq("bar") end end describe "when deep-merging between precedence levels" do it "correctly deep merges hashes and preserves the original contents" do @attributes.default = { "arglebargle" => { "foo" => "bar" } } @attributes.override = { "arglebargle" => { "fizz" => "buzz" } } expect(@attributes.merged_attributes[:arglebargle]).to eq({ "foo" => "bar", "fizz" => "buzz" }) expect(@attributes.default[:arglebargle]).to eq({ "foo" => "bar" }) expect(@attributes.override[:arglebargle]).to eq({ "fizz" => "buzz" }) end it "does not deep merge arrays, and preserves the original contents" do @attributes.default = { "arglebargle" => [ 1, 2, 3 ] } @attributes.override = { "arglebargle" => [ 4, 5, 6 ] } expect(@attributes.merged_attributes[:arglebargle]).to eq([ 4, 5, 6 ]) expect(@attributes.default[:arglebargle]).to eq([ 1, 2, 3 ]) expect(@attributes.override[:arglebargle]).to eq([ 4, 5, 6 ]) end it "correctly deep merges hashes and preserves the original contents when merging default and role_default" do @attributes.default = { "arglebargle" => { "foo" => "bar" } } @attributes.role_default = { "arglebargle" => { "fizz" => "buzz" } } expect(@attributes.merged_attributes[:arglebargle]).to eq({ "foo" => "bar", "fizz" => "buzz" }) expect(@attributes.default[:arglebargle]).to eq({ "foo" => "bar" }) expect(@attributes.role_default[:arglebargle]).to eq({ "fizz" => "buzz" }) end it "correctly deep merges arrays, and preserves the original contents when merging default and role_default" do @attributes.default = { "arglebargle" => [ 1, 2, 3 ] } @attributes.role_default = { "arglebargle" => [ 4, 5, 6 ] } expect(@attributes.merged_attributes[:arglebargle]).to eq([ 1, 2, 3, 4, 5, 6 ]) expect(@attributes.default[:arglebargle]).to eq([ 1, 2, 3 ]) expect(@attributes.role_default[:arglebargle]).to eq([ 4, 5, 6 ]) end end describe "when attemping to write without specifying precedence" do it "raises an error when using []=" do expect { @attributes[:new_key] = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end it "raises an error when using `attr=value`" do expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end end end chef-12.3.0/spec/unit/file_access_control_spec.rb0000644000004100000410000003011412520074675022011 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'ostruct' describe Chef::FileAccessControl do describe "Unix" do before do platform_mock :unix do # we have to re-load the file so the proper # platform specific module is mixed in @node = Chef::Node.new load File.join(File.dirname(__FILE__), "..", "..", "lib", "chef", "file_access_control.rb") @resource = Chef::Resource::File.new('/tmp/a_file.txt') @resource.owner('toor') @resource.group('wheel') @resource.mode('0400') @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @current_resource = Chef::Resource::File.new('/tmp/different_file.txt') @provider_requirements = Chef::Provider::ResourceRequirements.new(@resource, @run_context) @provider = double("File provider", :requirements => @provider_requirements, :manage_symlink_access? => false) @fac = Chef::FileAccessControl.new(@current_resource, @resource, @provider) end end describe 'class methods' do it 'responds to #writable?' do expect(Chef::FileAccessControl).to respond_to(:writable?) end end it "has a resource" do expect(@fac.resource).to equal(@resource) end it "has a file to manage" do expect(@fac.file).to eq('/tmp/different_file.txt') end it "is not modified yet" do expect(@fac).not_to be_modified end it "determines the uid of the owner specified by the resource" do expect(Etc).to receive(:getpwnam).with('toor').and_return(OpenStruct.new(:uid => 2342)) expect(@fac.target_uid).to eq(2342) end it "raises a Chef::Exceptions::UserIDNotFound error when Etc can't find the user's name" do expect(Etc).to receive(:getpwnam).with('toor').and_raise(ArgumentError) expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::UserIDNotFound, "cannot determine user id for 'toor', does the user exist on this system?") end it "does not attempt to resolve the uid if the user is not specified" do resource = Chef::Resource::File.new("a file") fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.target_uid).to be_nil end it "does not want to update the owner if none is specified" do resource = Chef::Resource::File.new("a file") fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.should_update_owner?).to be_falsey end it "raises an ArgumentError if the resource's owner is set to something wack" do @resource.instance_variable_set(:@owner, :diaf) expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(ArgumentError) end it "uses the resource's uid for the target uid when the resource's owner is specified by an integer" do @resource.owner(2342) expect(@fac.target_uid).to eq(2342) end it "wraps uids to their negative complements to correctly handle negative uids" do # More: Mac OS X (at least) has negative UIDs for 'nobody' and some other # users. Ruby doesn't believe in negative UIDs so you get the diminished radix # complement (i.e., it wraps around the maximum size of C unsigned int) of these # uids. So we have to get ruby and negative uids to smoke the peace pipe # with each other. @resource.owner('nobody') expect(Etc).to receive(:getpwnam).with('nobody').and_return(OpenStruct.new(:uid => (4294967294))) expect(@fac.target_uid).to eq(-2) end it "does not wrap uids to their negative complements beyond -9" do # More: when OSX userIDs are created by ActiveDirectory sync, it tends to use huge numbers # which had been incorrectly wrapped. It does not look like the OSX IDs go below -2 @resource.owner('bigdude') expect(Etc).to receive(:getpwnam).with('bigdude').and_return(OpenStruct.new(:uid => (4294967286))) expect(@fac.target_uid).to eq(4294967286) end it "wants to update the owner when the current owner is nil (creating a file)" do @current_resource.owner(nil) @resource.owner(2342) expect(@fac.should_update_owner?).to be_truthy end it "wants to update the owner when the current owner doesn't match desired" do @current_resource.owner(3224) @resource.owner(2342) expect(@fac.should_update_owner?).to be_truthy end it "includes updating ownership in its list of desired changes" do resource = Chef::Resource::File.new("a file") resource.owner(2342) @current_resource.owner(100) fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.describe_changes).to eq(["change owner from '100' to '2342'"]) end it "sets the file's owner as specified in the resource when the current owner is incorrect" do @resource.owner(2342) expect(File).to receive(:chown).with(2342, nil, '/tmp/different_file.txt') @fac.set_owner expect(@fac).to be_modified end it "doesn't set the file's owner if it already matches" do @resource.owner(2342) @current_resource.owner(2342) expect(File).not_to receive(:chown) @fac.set_owner expect(@fac).not_to be_modified end it "doesn't want to update a file's owner when it's already correct" do @resource.owner(2342) @current_resource.owner(2342) expect(@fac.should_update_owner?).to be_falsey end it "determines the gid of the group specified by the resource" do expect(Etc).to receive(:getgrnam).with('wheel').and_return(OpenStruct.new(:gid => 2342)) expect(@fac.target_gid).to eq(2342) end it "uses a user specified gid as the gid" do @resource.group(2342) expect(@fac.target_gid).to eq(2342) end it "raises a Chef::Exceptions::GroupIDNotFound error when Etc can't find the user's name" do expect(Etc).to receive(:getgrnam).with('wheel').and_raise(ArgumentError) expect { @fac.target_gid; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::GroupIDNotFound, "cannot determine group id for 'wheel', does the group exist on this system?") end it "does not attempt to resolve a gid when none is supplied" do resource = Chef::Resource::File.new('crab') fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.target_gid).to be_nil end it "does not want to update the group when no target group is specified" do resource = Chef::Resource::File.new('crab') fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.should_update_group?).to be_falsey end it "raises an error when the supplied group name is an alien" do @resource.instance_variable_set(:@group, :failburger) expect { @fac.target_gid; @provider_requirements.run(:create) }.to raise_error(ArgumentError) end it "wants to update the group when the current group is nil (creating a file)" do @resource.group(2342) @current_resource.group(nil) expect(@fac.should_update_group?).to be_truthy end it "wants to update the group when the current group doesn't match the target group" do @resource.group(2342) @current_resource.group(815) expect(@fac.should_update_group?).to be_truthy end it "includes updating the group in the list of changes" do resource = Chef::Resource::File.new('crab') resource.group(2342) @current_resource.group(815) fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.describe_changes).to eq(["change group from '815' to '2342'"]) end it "sets the file's group as specified in the resource when the group is not correct" do @resource.group(2342) @current_resource.group(815) expect(File).to receive(:chown).with(nil, 2342, '/tmp/different_file.txt') @fac.set_group expect(@fac).to be_modified end it "doesn't want to modify the file's group when the current group is correct" do @resource.group(2342) @current_resource.group(2342) expect(@fac.should_update_group?).to be_falsey end it "doesnt set the file's group if it is already correct" do @resource.group(2342) @current_resource.group(2342) # @fac.stub(:stat).and_return(OpenStruct.new(:gid => 2342)) expect(File).not_to receive(:chown) @fac.set_group expect(@fac).not_to be_modified end it "uses the supplied mode as octal when it's a string" do @resource.mode('444') expect(@fac.target_mode).to eq(292) # octal 444 => decimal 292 end it "uses the supplied mode verbatim when it's an integer" do @resource.mode(00444) expect(@fac.target_mode).to eq(292) end it "does not try to determine the mode when none is given" do resource = Chef::Resource::File.new('blahblah') fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.target_mode).to be_nil end it "doesn't want to update the mode when no target mode is given" do resource = Chef::Resource::File.new('blahblah') fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.should_update_mode?).to be_falsey end it "wants to update the mode when the current mode is nil (creating a file)" do @resource.mode("0400") @current_resource.mode(nil) expect(@fac.should_update_mode?).to be_truthy end it "wants to update the mode when the desired mode does not match the current mode" do @resource.mode("0400") @current_resource.mode("0644") expect(@fac.should_update_mode?).to be_truthy end it "includes changing the mode in the list of desired changes" do resource = Chef::Resource::File.new('blahblah') resource.mode("0750") @current_resource.mode("0444") fac = Chef::FileAccessControl.new(@current_resource, resource, @provider) expect(fac.describe_changes).to eq(["change mode from '0444' to '0750'"]) end it "sets the file's mode as specified in the resource when the current modes are incorrect" do # stat returns modes like 0100644 (octal) => 33188 (decimal) #@fac.stub(:stat).and_return(OpenStruct.new(:mode => 33188)) @current_resource.mode("0644") expect(File).to receive(:chmod).with(256, '/tmp/different_file.txt') @fac.set_mode expect(@fac).to be_modified end it "does not want to update the mode when the current mode is correct" do @current_resource.mode("0400") expect(@fac.should_update_mode?).to be_falsey end it "does not set the file's mode when the current modes are correct" do #@fac.stub(:stat).and_return(OpenStruct.new(:mode => 0100400)) @current_resource.mode("0400") expect(File).not_to receive(:chmod) @fac.set_mode expect(@fac).not_to be_modified end it "sets all access controls on a file" do allow(@fac).to receive(:stat).and_return(OpenStruct.new(:owner => 99, :group => 99, :mode => 0100444)) @resource.mode(0400) @resource.owner(0) @resource.group(0) expect(File).to receive(:chmod).with(0400, '/tmp/different_file.txt') expect(File).to receive(:chown).with(0, nil, '/tmp/different_file.txt') expect(File).to receive(:chown).with(nil, 0, '/tmp/different_file.txt') @fac.set_all expect(@fac).to be_modified end end end chef-12.3.0/spec/support/0000755000004100000410000000000012520074675015210 5ustar www-datawww-datachef-12.3.0/spec/support/platforms/0000755000004100000410000000000012520074675017217 5ustar www-datawww-datachef-12.3.0/spec/support/platforms/win32/0000755000004100000410000000000012520074675020161 5ustar www-datawww-datachef-12.3.0/spec/support/platforms/win32/spec_service.rb0000644000004100000410000000310212520074675023154 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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 'win32/daemon' class SpecService < ::Win32::Daemon def service_init @test_service_file = "#{ENV['TMP']}/spec_service_file" end def service_main(*startup_parameters) while running? do if !File.exists?(@test_service_file) File.open(@test_service_file, 'wb') do |f| f.write("This file is created by SpecService") end end sleep 1 end end ################################################################################ # Control Signal Callback Methods ################################################################################ def service_stop end def service_pause end def service_resume end def service_shutdown end end # To run this file as a service, it must be called as a script from within # the Windows Service framework. In that case, kick off the main loop! if __FILE__ == $0 SpecService.mainloop end chef-12.3.0/spec/support/platforms/prof/0000755000004100000410000000000012520074675020165 5ustar www-datawww-datachef-12.3.0/spec/support/platforms/prof/gc.rb0000644000004100000410000000311212520074675021100 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # module RSpec module Prof module GC class Profiler # GC 1 invokes. # Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC time(ms) # 1 0.012 159240 212940 10647 0.00000000000001530000 LINE_PATTERN = /^\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)\s+([\d\.]*)$/ def start ::GC::Profiler.enable unless ::GC::Profiler.enabled? end def stop ::GC::Profiler.disable end def working_set_size begin ::GC.start ::GC::Profiler.result.scan(LINE_PATTERN)[-1][2].to_i if ::GC::Profiler.enabled? ensure ::GC::Profiler.clear end end def handle_count 0 end end end end end chef-12.3.0/spec/support/platforms/prof/win32.rb0000644000004100000410000000221712520074675021456 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'chef/win32/process' module RSpec module Prof module Win32 class Profiler def start GC.start end def stop GC.start end def working_set_size Chef::ReservedNames::Win32::Process.get_current_process.memory_info[:WorkingSetSize] end def handle_count Chef::ReservedNames::Win32::Process.get_current_process.handle_count end end end end end chef-12.3.0/spec/support/chef_helpers.rb0000644000004100000410000000571012520074675020167 0ustar www-datawww-data# 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. # CHEF_SPEC_DATA = File.expand_path(File.dirname(__FILE__) + "/../data/") CHEF_SPEC_ASSETS = File.expand_path(File.dirname(__FILE__) + "/../functional/assets/") CHEF_SPEC_BACKUP_PATH = File.join(Dir.tmpdir, 'test-backup-path') Chef::Config[:log_level] = :fatal Chef::Config[:persistent_queue] = false Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH Chef::Log.init(StringIO.new) Chef::Log.level(Chef::Config.log_level) Chef::Config.solo(false) def sha256_checksum(path) Digest::SHA256.hexdigest(File.read(path)) end # From Ruby 1.9.2+ # Here for backwards compatibility with Ruby 1.8.7 # http://rubydoc.info/stdlib/tmpdir/1.9.2/Dir/Tmpname def make_tmpname(prefix_suffix, n = nil) case prefix_suffix when String prefix = prefix_suffix suffix = "" when Array prefix = prefix_suffix[0] suffix = prefix_suffix[1] else raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" end t = Time.now.strftime("%Y%m%d") path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" path << "-#{n}" if n path << suffix end # NOTE: # This is a temporary fix to get tests passing on systems that have no `diff` # until we can replace shelling out to `diff` with ruby diff-lcs def has_diff? begin diff_cmd = Mixlib::ShellOut.new("diff -v") diff_cmd.run_command true rescue Errno::ENOENT false end end # This is a helper to determine if the ruby in the PATH contains # win32/service gem. windows_service_manager tests create a windows # service that starts with the system ruby and requires this gem. def system_windows_service_gem? windows_service_gem_check_command = %q{ruby -r "win32/daemon" -e ":noop"} if defined?(Bundler) Bundler.with_clean_env do # This returns true if the gem can be loaded system(windows_service_gem_check_command) end else # This returns true if the gem can be loaded system(windows_service_gem_check_command) end end # This is a helper to canonicalize paths that we're using in the file # tests. def canonicalize_path(path) windows? ? path.gsub('/', '\\') : path end # Check if a cmd exists on the PATH def which(cmd) paths = ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ] paths.each do |path| filename = File.join(path, cmd) return filename if File.executable?(filename) end false end chef-12.3.0/spec/support/lib/0000755000004100000410000000000012520074675015756 5ustar www-datawww-datachef-12.3.0/spec/support/lib/chef/0000755000004100000410000000000012520074675016663 5ustar www-datawww-datachef-12.3.0/spec/support/lib/chef/resource/0000755000004100000410000000000012520074675020512 5ustar www-datawww-datachef-12.3.0/spec/support/lib/chef/resource/with_state.rb0000644000004100000410000000174212520074675023216 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 2010 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' require 'chef/json_compat' class Chef class Resource class WithState < Chef::Resource attr_accessor :state def initialize(name, run_context=nil) @resource_name = :with_state super end def state @state end end end end chef-12.3.0/spec/support/lib/chef/resource/zen_follower.rb0000644000004100000410000000201712520074675023544 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/knife' require 'chef/json_compat' class Chef class Resource class ZenFollower < Chef::Resource provides :follower, platform: "zen" def initialize(name, run_context=nil) @resource_name = :zen_follower super end def master(arg=nil) if !arg.nil? @master = arg end @master end end end end chef-12.3.0/spec/support/lib/chef/resource/cat.rb0000644000004100000410000000204112520074675021603 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef class Resource class Cat < Chef::Resource attr_accessor :action def initialize(name, run_context=nil) @resource_name = :cat super @action = "sell" end def pretty_kitty(arg=nil) if arg == true or arg == false @pretty_kitty = arg end @pretty_kitty end end end end chef-12.3.0/spec/support/lib/chef/resource/one_two_three_four.rb0000644000004100000410000000213712520074675024736 0ustar www-datawww-data# # Author:: John Hampton () # Copyright:: Copyright (c) 2009 CleanOffer, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource class OneTwoThreeFour < Chef::Resource attr_reader :i_can_count def initialize(name, run_context) @resource_name = :one_two_three_four super end def i_can_count(tf) @i_can_count = tf end def something(arg=nil) if arg == true or arg == false @something = arg end @something end end end end chef-12.3.0/spec/support/lib/chef/resource/zen_master.rb0000644000004100000410000000221212520074675023203 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 2010 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' require 'chef/json_compat' class Chef class Resource class ZenMaster < Chef::Resource attr_reader :peace def initialize(name, run_context=nil) @resource_name = :zen_master super allowed_actions << :win << :score end def peace(tf) @peace = tf end def something(arg=nil) if !arg.nil? @something = arg end @something end end end end chef-12.3.0/spec/support/lib/chef/provider/0000755000004100000410000000000012520074675020515 5ustar www-datawww-datachef-12.3.0/spec/support/lib/chef/provider/easy.rb0000644000004100000410000000161112520074675022002 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef class Provider class Easy < Chef::Provider def load_current_resource true end def action_sell true end def action_buy true end end end end chef-12.3.0/spec/support/lib/chef/provider/snakeoil.rb0000644000004100000410000000175712520074675022661 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef class Provider class SnakeOil < Chef::Provider def load_current_resource true end def action_purr @new_resource.updated_by_last_action(true) true end def action_sell true end def action_buy true end end end end chef-12.3.0/spec/support/lib/library_load_order.rb0000644000004100000410000000061012520074675022136 0ustar www-datawww-data# Helper module to track the load order of library files. # Used by `cookbook_compiler_spec.rb` # # This module must be loaded for any tests that load the cookbook # data/run_context/cookbooks/test to succeed. module LibraryLoadOrder extend self def load_order @load_order ||= [] end def reset! @load_order = nil end def record(file) load_order << file end end chef-12.3.0/spec/support/pedant/0000755000004100000410000000000012520074675016463 5ustar www-datawww-datachef-12.3.0/spec/support/pedant/Gemfile0000644000004100000410000000016012520074675017753 0ustar www-datawww-datasource "https://rubygems.org" gem 'chef-pedant', :github => 'opscode/chef-pedant', :ref => "server-cli-option" chef-12.3.0/spec/support/pedant/pedant_config.rb0000644000004100000410000001175512520074675021621 0ustar www-datawww-data# 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. # This annotated Pedant configuration file details the various # configuration settings available to you. It is separate from the # actual Pedant::Config class because not all settings have sane # defaults, and not all settings are appropriate in all settings. ################################################################################ # You MUST specify the address of the server the API requests will be # sent to. Only specify protocol, hostname, and port. # NOTE this is assigned in run_pedant.rb, because it's possible 8889 will not be the port chosen. #chef_server 'http://127.0.0.1:8889' # If you are doing development testing, you can specify the address of # the Solr server. The presence of this parameter will enable tests # to force commits to Solr, greatly decreasing the amount of time # needed for testing the search endpoint. This is only an # optimization for development! If you are testing a "live" Chef # Server, or otherwise do not have access to the Solr server from your # testing location, you should not specify a value for this parameter. # The tests will still run, albeit slower, as they will now need to # poll for a period to ensure they are querying committed results. #search_server "http://localhost:8983" # Related to the 'search_server' parameter, this specifies the maximum # amount of time (in seconds) that search endpoint requests should be # retried before giving up. If not explicitly set, it will default to # 65 seconds; only set it if you know that your Solr commit interval # differs significantly from this. maximum_search_time 0 # OSC sends erchef a host header with a port, so this option needs # # to be enabled for Pedant tests to work correctly explicit_port_url true # We're starting to break tests up into groups based on different # criteria. The proper API tests (the results of which are viewable # to OPC customers) should be the only ones run by Pedant embedded in # OPC installs. There are other specs that help us keep track of API # cruft that we want to come back and fix later; these shouldn't be # viewable to customers, but we should be able to run them in # development and CI environments. If this parameter is missing or # explicitly `false` only the customer-friendly tests will be run. # # This is mainly here for documentation purposes, since the # command-line `opscode-pedant` utility ultimately determines this # value. include_internal false # Test users. The five users specified below are required; their # names (:user, :non_org_user, etc.) are indicative of their role # within the tests. All users must have a ':name' key. If they have # a ':create_me' key, Pedant will create these users for you. If you # are using pre-existing users, you must supply a ':key_file' key, # which should be the fully-qualified path /on the machine Pedant is # running on/ to a private key for that user. key = 'spec/support/pedant/stickywicket.pem' superuser_name 'admin' superuser_key key webui_key key # When we updated Chef to RSpec 3 there were gem conflicts with chef-pedant. # We removed chef as a chef-pedant gem dependency in pedant.gemfile, but this # caused chef-pedant to fail because it could not query for the chef version # on the box pedant is running on. X-Chef-Version isn't needed in server # requests for these tests, so we've disabled it. ingore_x_chef_version true # Set the platform_class platform_class Pedant::OpenSourcePlatform requestors({ :clients => { # The the admin user, for the purposes of getting things rolling :admin => { :name => "pedant_admin_client", :create_me => true, :create_knife => true, :admin => true }, :non_admin => { :name => 'pedant_client', :create_me => true, :create_knife => true }, :bad => { :name => 'bad_client', :bogus => true } }, :users => { :admin => { :name => "admin", :key_file => key, :create_me => false, :create_knife => false, :admin => true }, :non_admin => { :name => "pedant_non_admin_user", :create_me => true, :create_knife => true, :admin => false }, # A user for Knife tests. A knife.rb and key files will be set up # for this user :knife_user => { :name => "knifey", :create_me => true, :create_knife => true } } }) self[:tags] = [:validation, :authentication, :authorization] verify_error_messages false chef-12.3.0/spec/support/pedant/stickywicket.pem0000644000004100000410000000321712520074675021706 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEApNCkX2k+lFGDWRVhX4uClaVQrumG9XXvk6X7M2izrIg7RzMP Dk4thhZkpx5gr22By7PZQdMEjWC/Zo8MBjtoJ0GV0jw8npefbU1MGKs2dtpYgo0N Fq8fX8MdFPu4h2W3g0dMEdhT8icc2H4EjhZmdeUhUn3RIEt2duCgp3YDYnUUZx3j N7MHcTIdzD58ikr6zQrZzHOv+OOI86Xk9EpyEEQizOLoQxkICNrhqN7ElQDuvXaX BSBrYDRKH2umBMMcXzvsR/SvkmqxoEESSpIlW8zeKAWQ+znNjDC0tmTg7jZmgSP7 siKrwo4t4ebjcmjpIoi/JKww/nGN3Uhz1ZOZuwIDAQABAoIBAQCaJQD2s0nyEeKU uKhfYe155Cl3zbWJcQnmv4AXbr9MiAVY6+oS6Q8ur1bn7kNjDzoruENjiuZhC7E3 TGZklb8tp+tluyy+7vQOmBKpp8fClSfewekR5CultqhGbb8B8yIVR+NfdUHd4rLZ z9KWyWB+txPZQQ8L80gSmrfmpzs3IuT7oPvmtBU1Wq9QapC4n/rUohHUpUV1du4G 0wCIF4zQTg6cbYW2YXozwVQvw+P7P3RVEqZt+aZlbVcy0fNr6jNao0hi1KFC9OH2 VjjU+PioreoA/NU3aZPIUzmJpWtsu31yuOZxXmytAkYooCZgiEQNEHnJlNPv0RmC 6BPMzVoBAoGBAM7yZoSNJpzdP/q1/4+H3zyy7o4I0VTW9u/GqUzhnbjm5poK30X9 YXh/7WOVV0OoVqdO6ljRKygP3Oggf41ZEbi1C6bbsO57pksBWgx9bD9V35XscZ0J F1ERe//kMHwVQy74R8/cIuRwm75haLSBj5/fwGbLeeVDglJkCVqPjtuBAoGBAMvh qsAGG5k9u6voTcXlFwS+B5YjULhK4NSxdJ2BnOxzYzxQ3IYQZMlb2xt8yZYx/ZZK wjkr9rcAPEQIQZ2A6NUbGq6qCD7sSmg6UAi0CgiqTokQ/Wtag0UDvFMzwerdg/On 37uxffpxpte8z1jYi/MxRaoTYueuc1UVnqofVIM7AoGBALZJzwPzUY/bVAADUJmd lYZiFsAGBF42/E05MOgH1GaK/ZWy/fkouDLsfK67XaK7JZk6ajLSDLG9R1kxRym6 y2FoGFtiKPfo8xIenrNhx3gCrG/jVjB9UYyXWiKNXifukr9M8/SkdBfFGWsZYqGd fmXVMiVaFoVcce8hLxwWWEABAoGBAKcyhKX/HEj6YFqlIoqkydDAylXs1jicZ27l rF2yum8KXZpMMdzbutuKsdAD8Ql0K6NB4a+jByuiTMn5/11cJxUEqkgM9sArZQW+ tH2+r+/VQpyTS0/rpXVGj/2nl2K1kI2T4R36e/aTl6CanWweAf9JK/lC9rxKyxg+ p6SaFuObAoGACP6TKCkp2oymXlKgdUUgPrnsaz2VAw8jD5QHtx10U4wty0C8gxsk MLe00h09iLPyFmvJpD+MgbxV/r6RrZeVdsKdU/5LG52YgiVSTaizyy+ciEfW7xoQ CL5EtZd8Cn5OKinBEzzFpELqunlqepIKCIDOcLKz/cjR+3a+E6Zx5Wo= -----END RSA PRIVATE KEY----- chef-12.3.0/spec/support/pedant/run_pedant.rb0000644000004100000410000000363112520074675021152 0ustar www-datawww-data#!/usr/bin/env ruby require 'bundler' require 'bundler/setup' require 'chef_zero/server' require 'rspec/core' require 'chef/chef_fs/chef_fs_data_store' require 'chef/chef_fs/config' require 'tmpdir' require 'fileutils' require 'chef/version' require 'chef/mixin/shell_out' def start_server(chef_repo_path) Dir.mkdir(chef_repo_path) if !File.exists?(chef_repo_path) # 11.6 and below had a bug where it couldn't create the repo children automatically if Chef::VERSION.to_f < 11.8 %w(clients cookbooks data_bags environments nodes roles users).each do |child| Dir.mkdir("#{chef_repo_path}/#{child}") if !File.exists?("#{chef_repo_path}/#{child}") end end # Start the new server Chef::Config.repo_mode = 'everything' Chef::Config.chef_repo_path = chef_repo_path Chef::Config.versioned_cookbooks = true chef_fs = Chef::ChefFS::Config.new.local_fs data_store = Chef::ChefFS::ChefFSDataStore.new(chef_fs) server = ChefZero::Server.new(:port => 8889.upto(9999), :data_store => data_store)#, :log_level => :debug) server.start_background server end tmpdir = Dir.mktmpdir begin # Create chef repository chef_repo_path = "#{tmpdir}/repo" # Capture setup data into master_chef_repo_path server = start_server(chef_repo_path) so = nil include Chef::Mixin::ShellOut Bundler.with_clean_env do shell_out("bundle install --gemfile spec/support/pedant/Gemfile", :live_stream => STDOUT) pedant_cmd = "chef-pedant " + " --config spec/support/pedant/pedant_config.rb" + " --server '#{server.url}'" + " --skip-knife --skip-validation --skip-authentication" + " --skip-authorization --skip-omnibus" so = shell_out("bundle exec #{pedant_cmd}", :live_stream => STDOUT, :env => {'BUNDLE_GEMFILE' => 'spec/support/pedant/Gemfile'}) end ensure server.stop if server && server.running? FileUtils.remove_entry_secure(tmpdir) if tmpdir end exit(so.exitstatus) chef-12.3.0/spec/support/mock/0000755000004100000410000000000012520074675016141 5ustar www-datawww-datachef-12.3.0/spec/support/mock/constant.rb0000644000004100000410000000277312520074675020330 0ustar www-datawww-data# Allows easy mocking of global and class constants # Inspired by: # http://missingbit.blogspot.com/2011/07/stubbing-constants-in-rspec_20.html # http://digitaldumptruck.jotabout.com/?p=551 def mock_constants(constants, &block) saved_constants = {} constants.each do |constant, val| source_object, const_name = parse_constant(constant) saved_constants[constant] = source_object.const_get(const_name) with_warnings(nil) {source_object.const_set(const_name, val) } end begin block.call ensure constants.each do |constant, val| source_object, const_name = parse_constant(constant) with_warnings(nil) { source_object.const_set(const_name, saved_constants[constant]) } end end end def parse_constant(constant) source, _, constant_name = constant.to_s.rpartition('::') [constantize(source), constant_name] end # Taken from ActiveSupport # File activesupport/lib/active_support/core_ext/kernel/reporting.rb, line 3 # # Sets $VERBOSE for the duration of the block and back to its original value afterwards. def with_warnings(flag) old_verbose, $VERBOSE = $VERBOSE, flag yield ensure $VERBOSE = old_verbose end # File activesupport/lib/active_support/inflector/methods.rb, line 209 def constantize(camel_cased_word) names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? constant = Object names.each do |name| constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) end constant end chef-12.3.0/spec/support/mock/platform.rb0000644000004100000410000000176012520074675020316 0ustar www-datawww-data# makes Chef think it's running on a certain platform..useful for unit testing # platform-specific functionality. # # If a block is given yields to the block with +RUBY_PLATFORM+ set to # 'i386-mingw32' (windows) or 'x86_64-darwin11.2.0' (unix). Usueful for # testing code that mixes in platform specific modules like +Chef::Mixin::Securable+ # or +Chef::FileAccessControl+ def platform_mock(platform = :unix, &block) allow(Chef::Platform).to receive(:windows?).and_return(platform == :windows ? true : false) ENV['SYSTEMDRIVE'] = (platform == :windows ? 'C:' : nil) if platform == :windows Chef::Config.set_defaults_for_windows else Chef::Config.set_defaults_for_nix end if block_given? mock_constants({"RUBY_PLATFORM" => (platform == :windows ? 'i386-mingw32' : 'x86_64-darwin11.2.0'), "File::PATH_SEPARATOR" => (platform == :windows ? ";" : ":"), "File::ALT_SEPARATOR" => (platform == :windows ? "\\" : nil) }) do yield end end end chef-12.3.0/spec/support/matchers/0000755000004100000410000000000012520074675017016 5ustar www-datawww-datachef-12.3.0/spec/support/matchers/leak.rb0000644000004100000410000000470112520074675020261 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # module Matchers module LeakBase include RSpec::Matchers def initialize(opts={}, &block) @warmup = opts[:warmup] || 5 @iterations = opts[:iterations] || 100 @variance = opts[:variance] || 5000 end def failure_message "expected final measure [#{@final_measure}] to be greater than or within +/- #{@variance} delta of initial measure [#{@initial_measure}]" end def failure_message_when_negated "expected final measure [#{@final_measure}] to be less than or within +/- #{@variance} delta of initial measure [#{@initial_measure}]" end private def match(measure, given_proc) profiler.start @initial_measure = 0 @final_measure = 0 @warmup.times do given_proc.call end @initial_measure = profiler.send(measure) @iterations.times do given_proc.call end profiler.stop @final_measure = profiler.send(measure) @final_measure > (@initial_measure + @variance) end def profiler @profiler ||= begin if Chef::Platform.windows? require File.join(File.dirname(__FILE__), '..', 'platforms', 'prof', 'win32') RSpec::Prof::Win32::Profiler.new else require File.join(File.dirname(__FILE__), '..', 'prof', 'gc') RSpec::Prof::GC::Profiler.new end end end end class LeakMemory include LeakBase def matches?(given_proc) match(:working_set_size, given_proc) end end class LeakHandles include LeakBase def matches?(given_proc) match(:handle_count, given_proc) end end def leak_memory(opts, &block) Matchers::LeakMemory.new(opts, &block) end def leak_handles(opts, &block) Matchers::LeakHandles.new(opts, &block) end end chef-12.3.0/spec/support/shared/0000755000004100000410000000000012520074675016456 5ustar www-datawww-datachef-12.3.0/spec/support/shared/context/0000755000004100000410000000000012520074675020142 5ustar www-datawww-datachef-12.3.0/spec/support/shared/context/config.rb0000644000004100000410000000101712520074675021733 0ustar www-datawww-data # # Define config file setups for spec tests here. # https://www.relishapp.com/rspec/rspec-core/docs/example-groups/shared-context # # Required chef files here: require 'chef/config' # Required spec files here: require 'spec_helper' # Basic config. Nothing fancy. shared_context "default config options" do before do Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef' end # Don't need to have an after block to reset the config... # The spec_helper.rb takes care of resetting the config state. end chef-12.3.0/spec/support/shared/matchers/0000755000004100000410000000000012520074675020264 5ustar www-datawww-datachef-12.3.0/spec/support/shared/matchers/exit_with_code.rb0000644000004100000410000000133112520074675023605 0ustar www-datawww-datarequire 'rspec/expectations' # Lifted from http://stackoverflow.com/questions/1480537/how-can-i-validate-exits-and-aborts-in-rspec RSpec::Matchers.define :exit_with_code do |exp_code| actual = nil match do |block| begin block.call rescue SystemExit => e actual = e.status end actual and actual == exp_code end failure_message do |block| "expected block to call exit(#{exp_code}) but exit" + (actual.nil? ? " not called" : "(#{actual}) was called") end failure_message_when_negated do |block| "expected block not to call exit(#{exp_code})" end description do "expect block to call exit(#{exp_code})" end def supports_block_expectations? true end end chef-12.3.0/spec/support/shared/matchers/match_environment_variable.rb0000644000004100000410000000073712520074675026205 0ustar www-datawww-data require 'rspec/expectations' require 'spec/support/platform_helpers' RSpec::Matchers.define :match_environment_variable do |varname| match do |actual| expected = if windows? && ENV[varname].nil? # On Windows, if an environment variable is not set, the command # `echo %VARNAME%` outputs %VARNAME% "%#{varname}%" else ENV[varname].to_s end actual == expected end end chef-12.3.0/spec/support/shared/integration/0000755000004100000410000000000012520074675021001 5ustar www-datawww-datachef-12.3.0/spec/support/shared/integration/app_server_support.rb0000644000004100000410000000232212520074675025267 0ustar www-datawww-data# # Author:: John Keiser () # Author:: Ho-Sheng Hsiao () # Copyright:: Copyright (c) 2012, 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 'rack' require 'stringio' module AppServerSupport def start_app_server(app, port) server = nil thread = Thread.new do Rack::Handler::WEBrick.run(app, :Port => 9018, :AccessLog => [], :Logger => WEBrick::Log::new(StringIO.new, 7) ) do |found_server| server = found_server end end Timeout::timeout(5) do until server && server.status == :Running sleep(0.01) end end [server, thread] end end chef-12.3.0/spec/support/shared/integration/knife_support.rb0000644000004100000410000001267112520074675024225 0ustar www-datawww-data# # Author:: John Keiser () # 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/config' require 'chef/knife' require 'chef/application/knife' require 'logger' require 'chef/log' module KnifeSupport DEBUG = ENV['DEBUG'] def knife(*args, &block) # Allow knife('role from file roles/blah.json') rather than requiring the # arguments to be split like knife('role', 'from', 'file', 'roles/blah.json') # If any argument will have actual spaces in it, the long form is required. # (Since knife commands always start with the command name, and command # names with spaces are always multiple args, this is safe.) if args.length == 1 args = args[0].split(/\s+/) end # Make output stable Chef::Config[:concurrency] = 1 # Work on machines where we can't access /var checksums_cache_dir = Dir.mktmpdir('checksums') do |checksums_cache_dir| Chef::Config[:cache_options] = { :path => checksums_cache_dir, :skip_expires => true } # This is Chef::Knife.run without load_commands--we'll load stuff # ourselves, thank you very much stdout = StringIO.new stderr = StringIO.new old_loggers = Chef::Log.loggers old_log_level = Chef::Log.level begin puts "knife: #{args.join(' ')}" if DEBUG subcommand_class = Chef::Knife.subcommand_class_from(args) subcommand_class.options = Chef::Application::Knife.options.merge(subcommand_class.options) subcommand_class.load_deps instance = subcommand_class.new(args) # Capture stdout/stderr instance.ui = Chef::Knife::UI.new(stdout, stderr, STDIN, {}) # Don't print stuff Chef::Config[:verbosity] = ( DEBUG ? 2 : 0 ) instance.config[:config_file] = File.join(CHEF_SPEC_DATA, "null_config.rb") # Configure chef with a (mostly) blank knife.rb # We set a global and then mutate it in our stub knife.rb so we can be # extra sure that we're not loading someone's real knife.rb and then # running test scenarios against a real chef server. If things don't # smell right, abort. $__KNIFE_INTEGRATION_FAILSAFE_CHECK = "ole" instance.configure_chef unless $__KNIFE_INTEGRATION_FAILSAFE_CHECK == "ole ole" raise Exception, "Potential misconfiguration of integration tests detected. Aborting test." end logger = Logger.new(stderr) logger.formatter = proc { |severity, datetime, progname, msg| "#{severity}: #{msg}\n" } Chef::Log.use_log_devices([logger]) Chef::Log.level = ( DEBUG ? :debug : :warn ) Chef::Log::Formatter.show_time = false instance.run_with_pretty_exceptions(true) exit_code = 0 # This is how rspec catches exit() rescue SystemExit => e exit_code = e.status ensure Chef::Log.use_log_devices(old_loggers) Chef::Log.level = old_log_level Chef::Config.delete(:cache_options) Chef::Config.delete(:concurrency) end KnifeResult.new(stdout.string, stderr.string, exit_code) end end private class KnifeResult include ::RSpec::Matchers def initialize(stdout, stderr, exit_code) @stdout = stdout @stderr = stderr @exit_code = exit_code end attr_reader :stdout attr_reader :stderr attr_reader :exit_code def should_fail(*args) expected = {} args.each do |arg| if arg.is_a?(Hash) expected.merge!(arg) elsif arg.is_a?(Integer) expected[:exit_code] = arg else expected[:stderr] = arg end end expected[:exit_code] = 1 if !expected[:exit_code] should_result_in(expected) end def should_succeed(*args) expected = {} args.each do |arg| if arg.is_a?(Hash) expected.merge!(arg) else expected[:stdout] = arg end end should_result_in(expected) end private def should_result_in(expected) expected[:stdout] = '' if !expected[:stdout] expected[:stderr] = '' if !expected[:stderr] expected[:exit_code] = 0 if !expected[:exit_code] # TODO make this go away stderr_actual = @stderr.sub(/^WARNING: No knife configuration file found\n/, '') if expected[:stderr].is_a?(Regexp) expect(stderr_actual).to match(expected[:stderr]) else expect(stderr_actual).to eq(expected[:stderr]) end stdout_actual = @stdout if Chef::Platform.windows? stderr_actual = stderr_actual.gsub("\r\n", "\n") stdout_actual = stdout_actual.gsub("\r\n", "\n") end expect(@exit_code).to eq(expected[:exit_code]) if expected[:stdout].is_a?(Regexp) expect(stdout_actual).to match(expected[:stdout]) else expect(stdout_actual).to eq(expected[:stdout]) end end end end chef-12.3.0/spec/support/shared/integration/integration_helper.rb0000644000004100000410000001052712520074675025215 0ustar www-datawww-data# # Author:: John Keiser () # Author:: Ho-Sheng Hsiao () # Copyright:: Copyright (c) 2012, 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 'tmpdir' require 'fileutils' require 'chef/config' require 'chef/json_compat' require 'chef/server_api' require 'chef_zero/rspec' require 'support/shared/integration/knife_support' require 'support/shared/integration/app_server_support' require 'spec_helper' module IntegrationSupport include ChefZero::RSpec module ClassMethods include ChefZero::RSpec def when_the_repository(desc, *tags, &block) context("when the chef repo #{desc}", *tags) do include_context "with a chef repo" module_eval(&block) end end def with_versioned_cookbooks(&block) context("with versioned cookbooks") do include_context "with versioned cookbooks" module_eval(&block) end end end def self.included(includer_class) includer_class.extend(ClassMethods) end def api Chef::ServerAPI.new end def directory(relative_path, &block) old_parent_path = @parent_path @parent_path = path_to(relative_path) FileUtils.mkdir_p(@parent_path) instance_eval(&block) if block @parent_path = old_parent_path end def file(relative_path, contents) filename = path_to(relative_path) dir = File.dirname(filename) FileUtils.mkdir_p(dir) unless dir == '.' File.open(filename, 'w') do |file| raw = case contents when Hash, Array Chef::JSONCompat.to_json_pretty(contents) else contents end file.write(raw) end end def symlink(relative_path, relative_dest) filename = path_to(relative_path) dir = File.dirname(filename) FileUtils.mkdir_p(dir) unless dir == '.' dest_filename = path_to(relative_dest) File.symlink(dest_filename, filename) end def path_to(relative_path) File.expand_path(relative_path, (@parent_path || @repository_dir)) end def cb_metadata(name, version, extra_text="") "name #{name.inspect}; version #{version.inspect}#{extra_text}" end def cwd(relative_path) @old_cwd = Dir.pwd Dir.chdir(path_to(relative_path)) end RSpec.shared_context "with a chef repo" do before :each do raise "Can only create one directory per test" if @repository_dir @repository_dir = Dir.mktmpdir('chef_repo') Chef::Config.chef_repo_path = @repository_dir %w(client cookbook data_bag environment node role user).each do |object_name| Chef::Config.delete("#{object_name}_path".to_sym) end end after :each do if @repository_dir begin %w(client cookbook data_bag environment node role user).each do |object_name| Chef::Config.delete("#{object_name}_path".to_sym) end Chef::Config.delete(:chef_repo_path) # TODO: "force" actually means "silence all exceptions". this # silences a weird permissions error on Windows that we should track # down, but for now there's no reason for it to blow up our CI. FileUtils.remove_entry_secure(@repository_dir, force=Chef::Platform.windows?) ensure @repository_dir = nil end end Dir.chdir(@old_cwd) if @old_cwd end end # Versioned cookbooks RSpec.shared_context 'with versioned cookbooks', :versioned_cookbooks => true do before(:each) { Chef::Config[:versioned_cookbooks] = true } after(:each) { Chef::Config.delete(:versioned_cookbooks) } end RSpec.shared_context "without versioned cookbooks", :versioned_cookbooks => false do # Just make sure this goes back to default before(:each) { Chef::Config[:versioned_cookbooks] = false } after(:each) { Chef::Config.delete(:versioned_cookbooks) } end end chef-12.3.0/spec/support/shared/unit/0000755000004100000410000000000012520074675017435 5ustar www-datawww-datachef-12.3.0/spec/support/shared/unit/windows_script_resource.rb0000644000004100000410000000543412520074675024755 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::WindowsScript) end context "when evaluating guards" do it "should have a default_guard_interpreter attribute that is the same as the resource" do expect(@resource.default_guard_interpreter).to eq(@resource.resource_name) end it "should default to using guard_interpreter attribute that is the same as the resource" do expect(@resource.guard_interpreter).to eq(@resource.resource_name) end it "should use a resource to evaluate the guard when guard_interpreter is not specified" do expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true) expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).not_to receive(:evaluate) @resource.only_if 'echo hi' expect(@resource.should_skip?(:run)).to eq(nil) end describe "when the guard is given a ruby block" do it "should evaluate the guard if the guard_interpreter is set to its default value" do @resource.only_if { true } expect(@resource.should_skip?(:run)).to eq(nil) end it "should raise an exception if the guard_interpreter is overridden from its default value" do @resource.guard_interpreter :bash @resource.only_if { true } expect { @resource.should_skip?(:run) }.to raise_error end end end context "script with a default guard interpreter" do let(:script_resource) do resource_instance.guard_interpreter :default resource_instance end it_should_behave_like "a script resource" end end chef-12.3.0/spec/support/shared/unit/resource/0000755000004100000410000000000012520074675021264 5ustar www-datawww-datachef-12.3.0/spec/support/shared/unit/resource/static_provider_resolution.rb0000644000004100000410000000431212520074675027275 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # This is for typical "static" provider resolution which maps resources onto # providers based only on the node data. Its not really 'static' because it # all goes through the Chef::ProviderResolver, but the effective result is # a static mapping for the node (unlike the service resource which is # complicated). # def static_provider_resolution(opts={}) action = opts[:action] provider_class = opts[:provider] resource_class = opts[:resource] name = opts[:name] os = opts[:os] platform_family = opts[:platform_family] platform_version = opts[:platform_version] describe resource_class, "static provider initialization" do let(:node) { node = Chef::Node.new node.automatic_attrs[:os] = os node.automatic_attrs[:platform_family] = platform_family node.automatic_attrs[:platform_version] = platform_version node } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:resource) { resource_class.new("foo", run_context) } it "should return a #{resource_class}" do expect(resource).to be_a_kind_of(resource_class) end it "should set the resource_name to #{name}" do expect(resource.resource_name).to eql(name) end it "should leave the provider nil" do expect(resource.provider).to eql(nil) end it "should resolve to a #{provider_class}" do expect(resource.provider_for_action(action)).to be_a(provider_class) end end end chef-12.3.0/spec/support/shared/unit/api_error_inspector.rb0000644000004100000410000001506412520074675024040 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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. # # == API Error Inspector Examples # These tests are work in progress. They exercise the code enough to ensure it # runs without error, but don't make assertions about the output. This is # because aspects such as how information gets formatted, what's included, etc. # are still in flux. When testing an inspector, change the outputter to use # STDOUT and manually check the output. shared_examples_for "an api error inspector" do before do @node_name = "test-node.example.com" @config = { :validation_client_name => "testorg-validator", :validation_key => "/etc/chef/testorg-validator.pem", :chef_server_url => "https://chef-api.example.com", :node_name => "testnode-name", :client_key => "/etc/chef/client.pem" } @description = Chef::Formatters::ErrorDescription.new("Error registering the node:") @outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR) #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR) end describe "when explaining a network error" do before do @exception = Errno::ECONNREFUSED.new("connection refused") @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 'private key missing' error" do before do @exception = Chef::Exceptions::PrivateKeyMissing.new("no private key yo") @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 401 caused by clock skew" do before do @response_body = "synchronize the clock on your host" @response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) unauthorized", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 401 (no clock skew)" do before do @response_body = "check your key and node name" @response = Net::HTTPUnauthorized.new("1.1", "401", "(response) unauthorized") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) unauthorized", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 403" do before do @response_body = "forbidden" @response = Net::HTTPForbidden.new("1.1", "403", "(response) forbidden") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) forbidden", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 400" do before do @response_body = "didn't like your data" @response = Net::HTTPBadRequest.new("1.1", "400", "(response) bad request") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) bad request", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 404" do before do @response_body = "probably caused by a redirect to a get" @response = Net::HTTPNotFound.new("1.1", "404", "(response) not found") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPServerException.new("(exception) not found", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 500" do before do @response_body = "sad trombone" @response = Net::HTTPInternalServerError.new("1.1", "500", "(response) internal server error") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPFatalError.new("(exception) internal server error", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining a 503" do before do @response_body = "sad trombone orchestra" @response = Net::HTTPBadGateway.new("1.1", "502", "(response) bad gateway") allow(@response).to receive(:body).and_return(@response_body) @exception = Net::HTTPFatalError.new("(exception) bad gateway", @response) @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end describe "when explaining an unknown error" do before do @exception = RuntimeError.new("(exception) something went wrong") @inspector = described_class.new(@node_name, @exception, @config) @inspector.add_explanation(@description) end it "prints a nice message" do @description.display(@outputter) end end end chef-12.3.0/spec/support/shared/unit/platform_introspector.rb0000644000004100000410000001673612520074675024436 0ustar www-datawww-data# # Author:: Seth Falcon () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010, 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. # shared_examples_for "a platform introspector" do before(:each) do @platform_hash = {} %w{openbsd freebsd}.each do |x| @platform_hash[x] = { "default" => x, "1.2.3" => "#{x}-1.2.3" } end @platform_hash["debian"] = {["5", "6"] => "debian-5/6", "default" => "debian"} @platform_hash["default"] = "default" # The following @platform_hash keys are used for testing version constraints @platform_hash['exact_match'] = { '1.2.3' => 'exact', '>= 1.0' => 'not exact'} @platform_hash['multiple_matches'] = { '~> 2.3.4' => 'matched ~> 2.3.4', '>= 2.3' => 'matched >=2.3' } @platform_hash['successful_matches'] = { '< 3.0' => 'matched < 3.0', '>= 3.0' => 'matched >= 3.0' } @platform_family_hash = { "debian" => "debian value", [:rhel, :fedora] => "redhatty value", "suse" => "suse value", :default => "default value" } end it "returns a default value when there is no known platform" do node = Hash.new expect(platform_introspector.value_for_platform(@platform_hash)).to eq("default") end it "returns a default value when there is no known platform family" do expect(platform_introspector.value_for_platform_family(@platform_family_hash)).to eq("default value") end it "returns a default value when the current platform doesn't match" do node.automatic_attrs[:platform] = "not-a-known-platform" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("default") end it "returns a default value when current platform_family doesn't match" do node.automatic_attrs[:platform_family] = "ultra-derived-linux" expect(platform_introspector.value_for_platform_family(@platform_family_hash)).to eq("default value") end it "returns a value based on the current platform" do node.automatic_attrs[:platform] = "openbsd" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("openbsd") end it "returns a value based on the current platform family" do node.automatic_attrs[:platform_family] = "debian" expect(platform_introspector.value_for_platform_family(@platform_family_hash)).to eq("debian value") end it "returns a version-specific value based on the current platform" do node.automatic_attrs[:platform] = "openbsd" node.automatic_attrs[:platform_version] = "1.2.3" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("openbsd-1.2.3") end it "returns a value based on the current platform if version not found" do node.automatic_attrs[:platform] = "openbsd" node.automatic_attrs[:platform_version] = "0.0.0" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("openbsd") end it 'returns the exact match' do node.automatic_attrs[:platform] = 'exact_match' node.automatic_attrs[:platform_version] = '1.2.3' expect(platform_introspector.value_for_platform(@platform_hash)).to eq('exact') end it 'raises RuntimeError' do node.automatic_attrs[:platform] = 'multiple_matches' node.automatic_attrs[:platform_version] = '2.3.4' expect {platform_introspector.value_for_platform(@platform_hash)}.to raise_error(RuntimeError) end it 'should return the value for that match' do node.automatic_attrs[:platform] = 'successful_matches' node.automatic_attrs[:platform_version] = '2.9' expect(platform_introspector.value_for_platform(@platform_hash)).to eq('matched < 3.0') end describe "when platform versions is an array" do it "returns a version-specific value based on the current platform" do node.automatic_attrs[:platform] = "debian" node.automatic_attrs[:platform_version] = "6" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("debian-5/6") end it "returns a value based on the current platform if version not found" do node.automatic_attrs[:platform] = "debian" node.automatic_attrs[:platform_version] = "0.0.0" expect(platform_introspector.value_for_platform(@platform_hash)).to eq("debian") end end describe "when checking platform?" do it "returns true if the node is a provided platform and platforms are provided as symbols" do node.automatic_attrs[:platform] = 'ubuntu' expect(platform_introspector.platform?([:redhat, :ubuntu])).to eq(true) end it "returns true if the node is a provided platform and platforms are provided as strings" do node.automatic_attrs[:platform] = 'ubuntu' expect(platform_introspector.platform?(["redhat", "ubuntu"])).to eq(true) end it "returns false if the node is not of the provided platforms" do node.automatic_attrs[:platform] = 'ubuntu' expect(platform_introspector.platform?(:splatlinux)).to eq(false) end end describe "when checking platform_family?" do it "returns true if the node is in a provided platform family and families are provided as symbols" do node.automatic_attrs[:platform_family] = 'debian' expect(platform_introspector.platform_family?([:rhel, :debian])).to eq(true) end it "returns true if the node is a provided platform and platforms are provided as strings" do node.automatic_attrs[:platform_family] = 'rhel' expect(platform_introspector.platform_family?(["rhel", "debian"])).to eq(true) end it "returns false if the node is not of the provided platforms" do node.automatic_attrs[:platform_family] = 'suse' expect(platform_introspector.platform_family?(:splatlinux)).to eq(false) end it "returns false if the node is not of the provided platforms and platform_family is not set" do expect(platform_introspector.platform_family?(:splatlinux)).to eq(false) end end # NOTE: this is a regression test for bug CHEF-1514 describe "when the value is an array" do before do @platform_hash = { "debian" => { "4.0" => [ :restart, :reload ], "default" => [ :restart, :reload, :status ] }, "ubuntu" => { "default" => [ :restart, :reload, :status ] }, "centos" => { "default" => [ :restart, :reload, :status ] }, "redhat" => { "default" => [ :restart, :reload, :status ] }, "fedora" => { "default" => [ :restart, :reload, :status ] }, "default" => { "default" => [:restart, :reload ] }} end it "returns the correct default for a given platform" do node.automatic_attrs[:platform] = "debian" node.automatic_attrs[:platform_version] = '9000' expect(platform_introspector.value_for_platform(@platform_hash)).to eq([ :restart, :reload, :status ]) end it "returns the correct platform+version specific value " do node.automatic_attrs[:platform] = "debian" node.automatic_attrs[:platform_version] = '4.0' expect(platform_introspector.value_for_platform(@platform_hash)).to eq([:restart, :reload]) end end end chef-12.3.0/spec/support/shared/unit/provider/0000755000004100000410000000000012520074675021267 5ustar www-datawww-datachef-12.3.0/spec/support/shared/unit/provider/file.rb0000644000004100000410000007630612520074675022547 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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 'tmpdir' if windows? require 'chef/win32/file' end # Filesystem stubs def file_symlink_class if windows? Chef::ReservedNames::Win32::File else File end end def normalized_path File.expand_path(resource_path) end # forwards-vs-reverse slashes on windows sucks def windows_path windows? ? normalized_path.gsub(/\\/, '/') : normalized_path end # this is all getting a bit stupid, CHEF-4802 cut to remove all this def setup_normal_file [ resource_path, normalized_path, windows_path].each do |path| allow(File).to receive(:file?).with(path).and_return(true) allow(File).to receive(:exists?).with(path).and_return(true) allow(File).to receive(:exist?).with(path).and_return(true) allow(File).to receive(:directory?).with(path).and_return(false) allow(File).to receive(:writable?).with(path).and_return(true) allow(file_symlink_class).to receive(:symlink?).with(path).and_return(false) allow(File).to receive(:realpath?).with(path).and_return(normalized_path) end allow(File).to receive(:directory?).with(enclosing_directory).and_return(true) end def setup_missing_file [ resource_path, normalized_path, windows_path].each do |path| allow(File).to receive(:file?).with(path).and_return(false) allow(File).to receive(:realpath?).with(path).and_return(resource_path) allow(File).to receive(:exists?).with(path).and_return(false) allow(File).to receive(:exist?).with(path).and_return(false) allow(File).to receive(:directory?).with(path).and_return(false) allow(File).to receive(:writable?).with(path).and_return(false) allow(file_symlink_class).to receive(:symlink?).with(path).and_return(false) end allow(File).to receive(:directory?).with(enclosing_directory).and_return(true) end def setup_symlink [ resource_path, normalized_path, windows_path].each do |path| allow(File).to receive(:file?).with(path).and_return(true) allow(File).to receive(:realpath?).with(path).and_return(normalized_path) allow(File).to receive(:exists?).with(path).and_return(true) allow(File).to receive(:exist?).with(path).and_return(true) allow(File).to receive(:directory?).with(path).and_return(false) allow(File).to receive(:writable?).with(path).and_return(true) allow(file_symlink_class).to receive(:symlink?).with(path).and_return(true) end allow(File).to receive(:directory?).with(enclosing_directory).and_return(true) end def setup_unwritable_file [ resource_path, normalized_path, windows_path].each do |path| allow(File).to receive(:file?).with(path).and_return(false) allow(File).to receive(:realpath?).with(path).and_raise(Errno::ENOENT) allow(File).to receive(:exists?).with(path).and_return(true) allow(File).to receive(:exist?).with(path).and_return(true) allow(File).to receive(:directory?).with(path).and_return(false) allow(File).to receive(:writable?).with(path).and_return(false) allow(file_symlink_class).to receive(:symlink?).with(path).and_return(false) end allow(File).to receive(:directory?).with(enclosing_directory).and_return(true) end def setup_missing_enclosing_directory [ resource_path, normalized_path, windows_path].each do |path| allow(File).to receive(:file?).with(path).and_return(false) allow(File).to receive(:realpath?).with(path).and_raise(Errno::ENOENT) allow(File).to receive(:exists?).with(path).and_return(false) allow(File).to receive(:exist?).with(path).and_return(false) allow(File).to receive(:directory?).with(path).and_return(false) allow(File).to receive(:writable?).with(path).and_return(false) allow(file_symlink_class).to receive(:symlink?).with(path).and_return(false) end allow(File).to receive(:directory?).with(enclosing_directory).and_return(false) end # A File subclass that we use as a replacement for Tempfile. Some versions of # Tempfile call `File.exist?()` internally which will cause test failures if # `File.exist?()` has been stubbed. class BasicTempfile < ::File def self.make_tmp_path(basename) slug = "#{basename}-#{rand(1 << 128)}" File.join(Dir.tmpdir, slug) end def self.new(basename) super(make_tmp_path(basename), File::RDWR|File::CREAT|File::EXCL, 0600) end def unlink self.class.unlink(path) end end shared_examples_for Chef::Provider::File do let(:tempfile_path) do end let!(:tempfile) do BasicTempfile.new("rspec-shared-file-provider") end before(:each) do allow(content).to receive(:tempfile).and_return(tempfile) allow(File).to receive(:exist?).with(tempfile.path).and_call_original allow(File).to receive(:exists?).with(tempfile.path).and_call_original end after do tempfile.close if (tempfile && !tempfile.closed?) File.unlink(tempfile.path) rescue nil end it "should return a #{described_class}" do expect(provider).to be_a_kind_of(described_class) end it "should store the resource passed to new as new_resource" do expect(provider.new_resource).to eql(resource) end it "should store the node passed to new as node" do expect(provider.node).to eql(node) end context "when loading the current resource" do context "when running load_current_resource" do # # the content objects need the current_resource to be loaded (esp remote_file), so calling # for content inside of load_current_resource is totally crossing the streams... # it "should not try to load the content when the file is present" do setup_normal_file expect(provider).not_to receive(:tempfile) expect(provider).not_to receive(:content) provider.load_current_resource end it "should not try to load the content when the file is missing" do setup_missing_file expect(provider).not_to receive(:tempfile) expect(provider).not_to receive(:content) provider.load_current_resource end end context "when running load_current_resource and the file exists" do before do setup_normal_file end let(:tempfile_sha256) { "42971f0ddce0cb20cf7660a123ffa1a1543beb2f1e7cd9d65858764a27f3201d" } it "should load a current resource based on the one specified at construction" do provider.load_current_resource expect(provider.current_resource).to be_a_kind_of(Chef::Resource::File) end it "the loaded current_resource name should be the same as the resource name" do provider.load_current_resource expect(provider.current_resource.name).to eql(resource.name) end it "the loaded current_resource path should be the same as the resoure path" do provider.load_current_resource expect(provider.current_resource.path).to eql(resource.path) end it "the loaded current_resource content should be nil" do provider.load_current_resource expect(provider.current_resource.content).to eql(nil) end it "it should call checksum if we are managing content" do expect(provider).to receive(:managing_content?).at_least(:once).and_return(true) expect(provider).to receive(:checksum).with(resource.path).and_return(tempfile_sha256) provider.load_current_resource end it "it should not call checksum if we are not managing content" do expect(provider).to receive(:managing_content?).at_least(:once).and_return(false) expect(provider).not_to receive(:checksum) provider.load_current_resource end end context "when running load_current_resource and the file does not exist" do before do setup_missing_file end it "the current_resource should be a Chef::Resource::File" do provider.load_current_resource expect(provider.current_resource).to be_a_kind_of(Chef::Resource::File) end it "the current_resource name should be the same as the resource name" do provider.load_current_resource expect(provider.current_resource.name).to eql(resource.name) end it "the current_resource path should be the same as the resource path" do provider.load_current_resource expect(provider.current_resource.path).to eql(resource.path) end it "the loaded current_resource content should be nil" do provider.load_current_resource expect(provider.current_resource.content).to eql(nil) end it "it should not call checksum if we are not managing content" do expect(provider).not_to receive(:managing_content?) expect(provider).not_to receive(:checksum) provider.load_current_resource end end context "examining file security metadata on Unix with a file that exists" do before do # fake that we're on unix even if we're on windows allow(Chef::Platform).to receive(:windows?).and_return(false) # mock up the filesystem to behave like unix setup_normal_file stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000) resource_real_path = File.realpath(resource.path) expect(File).to receive(:stat).with(resource_real_path).at_least(:once).and_return(stat_struct) allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", :name => "wheel")) allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", :name => "root")) end context "when the new_resource does not specify any state" do before do provider.load_current_resource end it "should load the permissions into the current_resource" do expect(provider.current_resource.mode).to eq("0600") expect(provider.current_resource.owner).to eq("root") expect(provider.current_resource.group).to eq("wheel") end it "should not set the new_resource permissions" do expect(provider.new_resource.group).to be_nil expect(provider.new_resource.owner).to be_nil expect(provider.new_resource.mode).to be_nil end end context "when the new_resource explicitly specifies resource state as numbers" do before do resource.owner(1) resource.group(1) resource.mode(0644) provider.load_current_resource end it "should load the permissions into the current_resource as numbers" do # Mode is always loaded as string for reporting purposes. expect(provider.current_resource.mode).to eq("0600") expect(provider.current_resource.owner).to eq(0) expect(provider.current_resource.group).to eq(0) end it "should not set the new_resource permissions" do expect(provider.new_resource.group).to eq(1) expect(provider.new_resource.owner).to eq(1) expect(provider.new_resource.mode).to eq(0644) end end context "when the new_resource explicitly specifies resource state as symbols" do before do resource.owner("macklemore") resource.group("seattlehiphop") resource.mode("0321") provider.load_current_resource end it "should load the permissions into the current_resource as symbols" do expect(provider.current_resource.mode).to eq("0600") expect(provider.current_resource.owner).to eq("root") expect(provider.current_resource.group).to eq("wheel") end it "should not set the new_resource permissions" do expect(provider.new_resource.group).to eq("seattlehiphop") expect(provider.new_resource.owner).to eq("macklemore") expect(provider.new_resource.mode).to eq("0321") end end end context "examining file security metadata on Unix with a file that does not exist" do before do # fake that we're on unix even if we're on windows allow(Chef::Platform).to receive(:windows?).and_return(false) setup_missing_file end context "when the new_resource does not specify any state" do before do provider.load_current_resource end it "the current_resource permissions should be nil" do expect(provider.current_resource.mode).to be_nil expect(provider.current_resource.owner).to be_nil expect(provider.current_resource.group).to be_nil end it "should not set the new_resource permissions" do expect(provider.new_resource.group).to be_nil expect(provider.new_resource.owner).to be_nil expect(provider.new_resource.mode).to be_nil end end context "when the new_resource explicitly specifies resource state" do before do resource.owner(63945) resource.group(51948) resource.mode(0123) provider.load_current_resource end it "the current_resource permissions should be nil" do expect(provider.current_resource.mode).to be_nil expect(provider.current_resource.owner).to be_nil expect(provider.current_resource.group).to be_nil end it "should not set the new_resource permissions" do expect(provider.new_resource.group).to eq(51948) expect(provider.new_resource.owner).to eq(63945) expect(provider.new_resource.mode).to eq(0123) end end end end context "when loading the new_resource after the run" do before do # fake that we're on unix even if we're on windows allow(Chef::Platform).to receive(:windows?).and_return(false) # mock up the filesystem to behave like unix setup_normal_file stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000) resource_real_path = File.realpath(resource.path) allow(File).to receive(:stat).with(resource_real_path).and_return(stat_struct) allow(Etc).to receive(:getgrgid).with(0).and_return(double("Group Ent", :name => "wheel")) allow(Etc).to receive(:getpwuid).with(0).and_return(double("User Ent", :name => "root")) provider.send(:load_resource_attributes_from_file, resource) end it "new_resource should record the new permission information" do expect(provider.new_resource.group).to eq("wheel") expect(provider.new_resource.owner).to eq("root") expect(provider.new_resource.mode).to eq("0600") end end context "when reporting security metadata on windows" do it "records the file owner" do skip end it "records rights for each user in the ACL" do skip end it "records deny_rights for each user in the ACL" do skip end end context "define_resource_requirements" do context "when the enclosing directory does not exist" do before { setup_missing_enclosing_directory } [:create, :create_if_missing, :touch].each do |action| context "action #{action}" do it "raises EnclosingDirectoryDoesNotExist" do expect {provider.run_action(action)}.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist) end it "does not raise an exception in why-run mode" do Chef::Config[:why_run] = true expect {provider.run_action(action)}.not_to raise_error Chef::Config[:why_run] = false end end end end context "when the file exists but is not deletable" do before { setup_unwritable_file } it "action delete raises InsufficientPermissions" do expect {provider.run_action(:delete)}.to raise_error(Chef::Exceptions::InsufficientPermissions) end it "action delete also raises InsufficientPermissions in why-run mode" do Chef::Config[:why_run] = true expect {provider.run_action(:delete)}.to raise_error(Chef::Exceptions::InsufficientPermissions) Chef::Config[:why_run] = false end end end context "action create" do it "should create the file, update its contents and then set the acls on the file" do setup_missing_file expect(provider).to receive(:do_create_file) expect(provider).to receive(:do_contents_changes) expect(provider).to receive(:do_acl_changes) expect(provider).to receive(:load_resource_attributes_from_file) provider.run_action(:create) end context "do_validate_content" do before { setup_normal_file } let(:tempfile) { t = double('Tempfile', :path => "/tmp/foo-bar-baz", :closed? => true) allow(content).to receive(:tempfile).and_return(t) t } let(:verification) { double("Verification") } context "with user-supplied verifications" do it "calls #verify on each verification with tempfile path" do allow(Chef::Resource::File::Verification).to receive(:new).and_return(verification) provider.new_resource.verify "true" provider.new_resource.verify "true" expect(verification).to receive(:verify).with(tempfile.path).twice.and_return(true) provider.send(:do_validate_content) end it "raises an exception if any verification fails" do provider.new_resource.verify "true" provider.new_resource.verify "false" allow(verification).to receive(:verify).with("true").and_return(true) allow(verification).to receive(:verify).with("false").and_return(false) expect{provider.send(:do_validate_content)}.to raise_error(Chef::Exceptions::ValidationFailed) end end end context "do_create_file" do context "when the file exists" do before { setup_normal_file } it "should not create the file" do provider.load_current_resource expect(provider.deployment_strategy).not_to receive(:create).with(resource_path) provider.send(:do_create_file) expect(provider.send(:needs_creating?)).to eq(false) end end context "when the file does not exist" do before { setup_missing_file } it "should create the file" do provider.load_current_resource expect(provider.deployment_strategy).to receive(:create).with(resource_path) provider.send(:do_create_file) expect(provider.send(:needs_creating?)).to eq(true) end end end context "do_contents_changes" do context "when there is content to deploy" do before do setup_normal_file provider.load_current_resource tempfile = double('Tempfile', :path => "/tmp/foo-bar-baz") allow(content).to receive(:tempfile).and_return(tempfile) expect(File).to receive(:exists?).with("/tmp/foo-bar-baz").and_return(true) expect(tempfile).to receive(:close).once expect(tempfile).to receive(:unlink).once end context "when the contents have changed" do let(:tempfile_path) { "/tmp/foo-bar-baz" } let(:tempfile_sha256) { "42971f0ddce0cb20cf7660a123ffa1a1543beb2f1e7cd9d65858764a27f3201d" } let(:diff_for_reporting) { "+++\n---\n+foo\n-bar\n" } before do allow(provider).to receive(:contents_changed?).and_return(true) diff = double('Diff', :for_output => ['+++','---','+foo','-bar'], :for_reporting => diff_for_reporting ) allow(diff).to receive(:diff).with(resource_path, tempfile_path).and_return(true) expect(provider).to receive(:diff).at_least(:once).and_return(diff) expect(provider).to receive(:managing_content?).at_least(:once).and_return(true) expect(provider).to receive(:checksum).with(tempfile_path).and_return(tempfile_sha256) expect(provider).to receive(:checksum).with(resource_path).and_return(tempfile_sha256) expect(provider.deployment_strategy).to receive(:deploy).with(tempfile_path, normalized_path) end context "when the file was created" do before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(true) } it "does not backup the file and does not produce a diff for reporting" do expect(provider).not_to receive(:do_backup) provider.send(:do_contents_changes) expect(resource.diff).to be_nil end end context "when the file was not created" do before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(false) } it "backs up the file and produces a diff for reporting" do expect(provider).to receive(:do_backup) provider.send(:do_contents_changes) expect(resource.diff).to eq(diff_for_reporting) end end end it "does nothing when the contents have not changed" do allow(provider).to receive(:contents_changed?).and_return(false) expect(provider).not_to receive(:diff) provider.send(:do_contents_changes) end end it "does nothing when there is no content to deploy (tempfile returned from contents is nil)" do expect(provider.send(:content)).to receive(:tempfile).at_least(:once).and_return(nil) expect(provider).not_to receive(:diff) expect{ provider.send(:do_contents_changes) }.not_to raise_error end it "raises an exception when the content object returns a tempfile with a nil path" do tempfile = double('Tempfile', :path => nil) expect(provider.send(:content)).to receive(:tempfile).at_least(:once).and_return(tempfile) expect{ provider.send(:do_contents_changes) }.to raise_error end it "raises an exception when the content object returns a tempfile that does not exist" do tempfile = double('Tempfile', :path => "/tmp/foo-bar-baz") expect(provider.send(:content)).to receive(:tempfile).at_least(:once).and_return(tempfile) expect(File).to receive(:exists?).with("/tmp/foo-bar-baz").and_return(false) expect{ provider.send(:do_contents_changes) }.to raise_error end end context "do_acl_changes" do it "needs tests" do skip end end context "do_selinux" do context "when resource is updated" do before do setup_normal_file provider.load_current_resource allow(provider).to receive(:resource_updated?).and_return(true) end it "should check for selinux_enabled? by default" do expect(provider).to receive(:selinux_enabled?) provider.send(:do_selinux) end context "when selinux fixup is enabled in the config" do before do @original_selinux_fixup = Chef::Config[:enable_selinux_file_permission_fixup] Chef::Config[:enable_selinux_file_permission_fixup] = true end after do Chef::Config[:enable_selinux_file_permission_fixup] = @original_selinux_fixup end context "when selinux is enabled on the system" do before do expect(provider).to receive(:selinux_enabled?).and_return(true) end it "restores security context on the file" do expect(provider).to receive(:restore_security_context).with(normalized_path, false) provider.send(:do_selinux) end it "restores security context recursively when told so" do expect(provider).to receive(:restore_security_context).with(normalized_path, true) provider.send(:do_selinux, true) end end context "when selinux is disabled on the system" do before do expect(provider).to receive(:selinux_enabled?).and_return(false) end it "should not restore security context" do expect(provider).not_to receive(:restore_security_context) provider.send(:do_selinux) end end end context "when selinux fixup is disabled in the config" do before do @original_selinux_fixup = Chef::Config[:enable_selinux_file_permission_fixup] Chef::Config[:enable_selinux_file_permission_fixup] = false end after do Chef::Config[:enable_selinux_file_permission_fixup] = @original_selinux_fixup end it "should not check for selinux_enabled?" do expect(provider).not_to receive(:selinux_enabled?) provider.send(:do_selinux) end end end context "when resource is not updated" do before do allow(provider).to receive(:resource_updated?).and_return(false) end it "should not check for selinux_enabled?" do expect(provider).not_to receive(:selinux_enabled?) provider.send(:do_selinux) end end end end context "action delete" do context "when the file exists" do context "when the file is writable" do context "when the file is not a symlink" do before { setup_normal_file } it "should backup and delete the file and be updated by the last action" do expect(provider).to receive(:do_backup).at_least(:once).and_return(true) expect(File).to receive(:delete).with(resource_path).and_return(true) provider.run_action(:delete) expect(resource).to be_updated_by_last_action end end context "when the file is a symlink" do before { setup_symlink } it "should not backup the symlink" do expect(provider).not_to receive(:do_backup) expect(File).to receive(:delete).with(resource_path).and_return(true) provider.run_action(:delete) expect(resource).to be_updated_by_last_action end end end context "when the file is not writable" do before { setup_unwritable_file } it "should not try to backup or delete the file, and should not be updated by last action" do expect(provider).not_to receive(:do_backup) expect(File).not_to receive(:delete) expect { provider.run_action(:delete) }.to raise_error() expect(resource).not_to be_updated_by_last_action end end end context "when the file does not exist" do before { setup_missing_file } it "should not try to backup or delete the file, and should not be updated by last action" do expect(provider).not_to receive(:do_backup) expect(File).not_to receive(:delete) expect { provider.run_action(:delete) }.not_to raise_error expect(resource).not_to be_updated_by_last_action end end end context "action touch" do context "when the file does not exist" do before { setup_missing_file } it "should update the atime/mtime on action_touch" do expect(File).to receive(:utime).once expect(provider).to receive(:action_create) provider.run_action(:touch) expect(resource).to be_updated_by_last_action end end context "when the file exists" do before { setup_normal_file } it "should update the atime/mtime on action_touch" do expect(File).to receive(:utime).once expect(provider).to receive(:action_create) provider.run_action(:touch) expect(resource).to be_updated_by_last_action end end end context "action create_if_missing" do context "when the file does not exist" do before { setup_missing_file } it "should call action_create" do expect(provider).to receive(:action_create) provider.run_action(:create_if_missing) end end context "when the file exists" do before { setup_normal_file } it "should not call action_create" do expect(provider).not_to receive(:action_create) provider.run_action(:create_if_missing) end end end end shared_examples_for "a file provider with content field" do context "when testing managing_content?" do it "should be false when creating a file without content" do provider.action = :create allow(resource).to receive(:content).and_return(nil) allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_falsey end it "should be true when creating a file with content" do provider.action = :create allow(resource).to receive(:content).and_return("flurbleblobbleblooble") allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_truthy end it "should be true when checksum is set on the content (no matter how crazy)" do provider.action = :create_if_missing allow(resource).to receive(:checksum).and_return("1234123234234234") allow(resource).to receive(:content).and_return(nil) expect(provider.send(:managing_content?)).to be_truthy end it "should be false when action is create_if_missing" do provider.action = :create_if_missing allow(resource).to receive(:content).and_return("flurbleblobbleblooble") allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_falsey end end end shared_examples_for "a file provider with source field" do context "when testing managing_content?" do it "should be false when creating a file without content" do provider.action = :create allow(resource).to receive(:content).and_return(nil) allow(resource).to receive(:source).and_return(nil) allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_falsey end it "should be true when creating a file with content" do provider.action = :create allow(resource).to receive(:content).and_return(nil) allow(resource).to receive(:source).and_return("http://somewhere.com/something.php") allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_truthy end it "should be true when checksum is set on the content (no matter how crazy)" do provider.action = :create_if_missing allow(resource).to receive(:content).and_return(nil) allow(resource).to receive(:source).and_return(nil) allow(resource).to receive(:checksum).and_return("1234123234234234") expect(provider.send(:managing_content?)).to be_truthy end it "should be false when action is create_if_missing" do provider.action = :create_if_missing allow(resource).to receive(:content).and_return(nil) allow(resource).to receive(:source).and_return("http://somewhere.com/something.php") allow(resource).to receive(:checksum).and_return(nil) expect(provider.send(:managing_content?)).to be_falsey end end end chef-12.3.0/spec/support/shared/unit/provider/useradd_based_user_provider.rb0000644000004100000410000004206012520074675027353 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010, 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. # shared_examples_for "a useradd-based user provider" do |supported_useradd_options| before(:each) do @node = Chef::Node.new @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::User.new("adam", @run_context) @new_resource.comment "Adam Jacob" @new_resource.uid 1000 @new_resource.gid 1000 @new_resource.home "/home/adam" @new_resource.shell "/usr/bin/zsh" @new_resource.password "abracadabra" @new_resource.system false @new_resource.manage_home false @new_resource.force false @new_resource.non_unique false @current_resource = Chef::Resource::User.new("adam", @run_context) @current_resource.comment "Adam Jacob" @current_resource.uid 1000 @current_resource.gid 1000 @current_resource.home "/home/adam" @current_resource.shell "/usr/bin/zsh" @current_resource.password "abracadabra" @current_resource.system false @current_resource.manage_home false @current_resource.force false @current_resource.non_unique false @current_resource.supports({:manage_home => false, :non_unique => false}) end describe "when setting option" do supported_useradd_options.each do |attribute, option| it "should check for differences in #{attribute} between the new and current resources" do expect(@current_resource).to receive(attribute) expect(@new_resource).to receive(attribute) provider.universal_options end it "should set the option for #{attribute} if the new resources #{attribute} is not nil" do allow(@new_resource).to receive(attribute).and_return("hola") expect(provider.universal_options).to eql([option, 'hola']) end it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management" do allow(@new_resource).to receive(:supports).and_return({:manage_home => false, :non_unique => false}) allow(@new_resource).to receive(attribute).and_return("hola") expect(provider.universal_options).to eql([option, 'hola']) end it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management (using real attributes)" do allow(@new_resource).to receive(:manage_home).and_return(false) allow(@new_resource).to receive(:non_unique).and_return(false) allow(@new_resource).to receive(:non_unique).and_return(false) allow(@new_resource).to receive(attribute).and_return("hola") expect(provider.universal_options).to eql([option, 'hola']) end end it "should combine all the possible options" do combined_opts = [] supported_useradd_options.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option| allow(@new_resource).to receive(attribute).and_return("hola") combined_opts << option << 'hola' end expect(provider.universal_options).to eql(combined_opts) end describe "when we want to create a system user" do before do @new_resource.manage_home(true) @new_resource.non_unique(false) end it "should set useradd -r" do @new_resource.system(true) expect(provider.useradd_options).to eq([ "-r" ]) end end describe "when the resource has a different home directory and supports home directory management" do before do allow(@new_resource).to receive(:home).and_return("/wowaweea") allow(@new_resource).to receive(:supports).and_return({:manage_home => true, :non_unique => false}) end it "should set -m -d /homedir" do expect(provider.universal_options).to eq(%w[-d /wowaweea -m]) expect(provider.useradd_options).to eq([]) end end describe "when the resource has a different home directory and supports home directory management (using real attributes)" do before do allow(@new_resource).to receive(:home).and_return("/wowaweea") allow(@new_resource).to receive(:manage_home).and_return(true) allow(@new_resource).to receive(:non_unique).and_return(false) end it "should set -m -d /homedir" do expect(provider.universal_options).to eql(%w[-d /wowaweea -m]) expect(provider.useradd_options).to eq([]) end end describe "when the resource supports non_unique ids" do before do allow(@new_resource).to receive(:supports).and_return({:manage_home => false, :non_unique => true}) end it "should set -m -o" do expect(provider.universal_options).to eql([ "-o" ]) end end describe "when the resource supports non_unique ids (using real attributes)" do before do allow(@new_resource).to receive(:manage_home).and_return(false) allow(@new_resource).to receive(:non_unique).and_return(true) end it "should set -m -o" do expect(provider.universal_options).to eql([ "-o" ]) end end end describe "when creating a user" do before(:each) do @current_resource = Chef::Resource::User.new(@new_resource.name, @run_context) @current_resource.username(@new_resource.username) provider.current_resource = @current_resource provider.new_resource.manage_home true provider.new_resource.home "/Users/mud" provider.new_resource.gid '23' end it "runs useradd with the computed command options" do command = ["useradd", "-c", 'Adam Jacob', "-g", '23' ] command.concat(["-p", 'abracadabra']) if supported_useradd_options.key?("password") command.concat([ "-s", '/usr/bin/zsh', "-u", '1000', "-d", '/Users/mud', "-m", "adam" ]) expect(provider).to receive(:shell_out!).with(*command).and_return(true) provider.create_user end describe "and home is not specified for new system user resource" do before do provider.new_resource.system true # there is no public API to set attribute's value to nil provider.new_resource.instance_variable_set("@home", nil) end it "should not include -m or -d in the command options" do command = ["useradd", "-c", 'Adam Jacob', "-g", '23'] command.concat(["-p", 'abracadabra']) if supported_useradd_options.key?("password") command.concat([ "-s", '/usr/bin/zsh', "-u", '1000', "-r", "adam" ]) expect(provider).to receive(:shell_out!).with(*command).and_return(true) provider.create_user end end end describe "when managing a user" do before(:each) do provider.new_resource.manage_home true provider.new_resource.home "/Users/mud" provider.new_resource.gid '23' end # CHEF-3423, -m must come before the username # CHEF-4305, -d must come before -m to support CentOS/RHEL 5 it "runs usermod with the computed command options" do command = ["usermod", "-g", '23', "-d", '/Users/mud', "-m", "adam" ] expect(provider).to receive(:shell_out!).with(*command).and_return(true) provider.manage_user end it "does not set the -r option to usermod" do @new_resource.system(true) command = ["usermod", "-g", '23', "-d", '/Users/mud', "-m", "adam" ] expect(provider).to receive(:shell_out!).with(*command).and_return(true) provider.manage_user end it "CHEF-3429: does not set -m if we aren't changing the home directory" do expect(provider).to receive(:updating_home?).and_return(false) command = ["usermod", "-g", '23', "adam" ] expect(provider).to receive(:shell_out!).with(*command).and_return(true) provider.manage_user end end describe "when removing a user" do it "should run userdel with the new resources user name" do expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true) provider.remove_user end it "should run userdel with the new resources user name and -r if manage_home is true" do @new_resource.supports({ :manage_home => true, :non_unique => false}) expect(provider).to receive(:shell_out!).with("userdel", "-r", @new_resource.username).and_return(true) provider.remove_user end it "should run userdel with the new resources user name if non_unique is true" do @new_resource.supports({ :manage_home => false, :non_unique => true}) expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true) provider.remove_user end it "should run userdel with the new resources user name and -f if force is true" do @new_resource.force(true) expect(provider).to receive(:shell_out!).with("userdel", "-f", @new_resource.username).and_return(true) provider.remove_user end end describe "when checking the lock" do # lazy initialize so we can modify stdout and stderr strings let(:passwd_s_status) do double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr) end before(:each) do # @node = Chef::Node.new # @new_resource = double("Chef::Resource::User", # :nil_object => true, # :username => "adam" # ) #provider = Chef::Provider::User::Useradd.new(@node, @new_resource) @stdout = "root P 09/02/2008 0 99999 7 -1" @stderr = "" end it "should return false if status begins with P" do expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) expect(provider.check_lock).to eql(false) end it "should return false if status begins with N" do @stdout = "root N" expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) expect(provider.check_lock).to eql(false) end it "should return true if status begins with L" do @stdout = "root L" expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) expect(provider.check_lock).to eql(true) end it "should raise a Chef::Exceptions::User if passwd -S fails on anything other than redhat/centos" do @node.automatic_attrs[:platform] = 'ubuntu' expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) expect(passwd_s_status).to receive(:exitstatus).and_return(1) expect { provider.check_lock }.to raise_error(Chef::Exceptions::User) end ['redhat', 'centos'].each do |os| it "should not raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is version 0.73-1" do @node.automatic_attrs[:platform] = os expect(passwd_s_status).to receive(:exitstatus).and_return(1) expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-1\n", :stderr => "") expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status) expect { provider.check_lock }.not_to raise_error end it "should raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is not version 0.73-1" do @node.automatic_attrs[:platform] = os expect(passwd_s_status).to receive(:exitstatus).and_return(1) expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_s_status) rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-2\n", :stderr => "") expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status) expect { provider.check_lock }.to raise_error(Chef::Exceptions::User) end it "should raise a ShellCommandFailed exception if passwd -S exits with something other than 0 or 1 on #{os}" do @node.automatic_attrs[:platform] = os expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed) expect { provider.check_lock }.to raise_error(Mixlib::ShellOut::ShellCommandFailed) end end context "when in why run mode" do before do passwd_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "", :stderr => "passwd: user 'chef-test' does not exist\n") expect(provider).to receive(:shell_out!). with("passwd", "-S", @new_resource.username, {:returns=>[0, 1]}). and_return(passwd_status) Chef::Config[:why_run] = true end it "should return false if the user does not exist" do expect(provider.check_lock).to eql(false) end it "should not raise an error if the user does not exist" do expect { provider.check_lock }.not_to raise_error end end end describe "when locking the user" do it "should run usermod -L with the new resources username" do expect(provider).to receive(:shell_out!).with("usermod", "-L", @new_resource.username) provider.lock_user end end describe "when unlocking the user" do it "should run usermod -L with the new resources username" do expect(provider).to receive(:shell_out!).with("usermod", "-U", @new_resource.username) provider.unlock_user end end describe "when checking if home needs updating" do [ { "action" => "should return false if home matches", "current_resource_home" => [ "/home/laurent" ], "new_resource_home" => [ "/home/laurent" ], "expected_result" => false }, { "action" => "should return true if home doesn't match", "current_resource_home" => [ "/home/laurent" ], "new_resource_home" => [ "/something/else" ], "expected_result" => true }, { "action" => "should return false if home only differs by trailing slash", "current_resource_home" => [ "/home/laurent" ], "new_resource_home" => [ "/home/laurent/", "/home/laurent" ], "expected_result" => false }, { "action" => "should return false if home is an equivalent path", "current_resource_home" => [ "/home/laurent" ], "new_resource_home" => [ "/home/./laurent", "/home/laurent" ], "expected_result" => false }, ].each do |home_check| it home_check["action"] do provider.current_resource.home home_check["current_resource_home"].first @current_home_mock = double("Pathname") provider.new_resource.home home_check["new_resource_home"].first @new_home_mock = double("Pathname") expect(Pathname).to receive(:new).with(@current_resource.home).and_return(@current_home_mock) expect(@current_home_mock).to receive(:cleanpath).and_return(home_check["current_resource_home"].last) expect(Pathname).to receive(:new).with(@new_resource.home).and_return(@new_home_mock) expect(@new_home_mock).to receive(:cleanpath).and_return(home_check["new_resource_home"].last) expect(provider.updating_home?).to eq(home_check["expected_result"]) end end it "should return true if the current home does not exist but a home is specified by the new resource" do @new_resource = Chef::Resource::User.new("adam", @run_context) @current_resource = Chef::Resource::User.new("adam", @run_context) provider = Chef::Provider::User::Useradd.new(@new_resource, @run_context) provider.current_resource = @current_resource @current_resource.home nil @new_resource.home "/home/kitten" expect(provider.updating_home?).to eq(true) end end end chef-12.3.0/spec/support/shared/unit/script_resource.rb0000644000004100000410000000737312520074675023207 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 it "should create a new Chef::Resource::Script" do expect(script_resource).to be_a_kind_of(Chef::Resource) expect(script_resource).to be_a_kind_of(Chef::Resource::Script) end it "should have a resource name of :script" do expect(script_resource.resource_name).to eql(resource_name) end it "should set command to nil on the resource", :chef_gte_13_only do expect(script_resource.command).to be nil end it "should set command to the name on the resource", :chef_lt_13_only do expect(script_resource.command).to eql script_resource.name end it "should accept a string for the code" do script_resource.code "hey jude" expect(script_resource.code).to eql("hey jude") end it "should accept a string for the flags" do script_resource.flags "-f" expect(script_resource.flags).to eql("-f") end it "should raise an exception if users set command on the resource", :chef_gte_13_only do expect { script_resource.command('foo') }.to raise_error(Chef::Exceptions::Script) end it "should not raise an exception if users set command on the resource", :chef_lt_13_only do expect { script_resource.command('foo') }.not_to raise_error end describe "when executing guards" do let(:resource) { resource = script_resource resource.run_context = run_context resource.code 'echo hi' resource } let(:node) { node = Chef::Node.new node.automatic[:platform] = "debian" node.automatic[:platform_version] = "6.0" node } let(:events) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, events) } it "inherits exactly the :cwd, :environment, :group, :path, :user, and :umask attributes from a parent resource class" do inherited_difference = Chef::Resource::Script.guard_inherited_attributes - [:cwd, :environment, :group, :path, :user, :umask ] expect(inherited_difference).to eq([]) end it "when guard_interpreter is set to the default value, the guard command string should be evaluated by command execution and not through a resource" do expect_any_instance_of(Chef::Resource::Conditional).not_to receive(:evaluate_block) expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).not_to receive(:evaluate_action) expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:evaluate).and_return(true) resource.only_if 'echo hi' expect(resource.should_skip?(:run)).to eq(nil) end it "when a valid guard_interpreter resource is specified, a block should be used to evaluate the guard" do expect_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).not_to receive(:evaluate) expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true) resource.guard_interpreter :script resource.only_if 'echo hi' expect(resource.should_skip?(:run)).to eq(nil) end end end chef-12.3.0/spec/support/shared/unit/file_system_support.rb0000644000004100000410000000433212520074675024103 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system' require 'chef/chef_fs/file_system/memory_root' require 'chef/chef_fs/file_system/memory_dir' require 'chef/chef_fs/file_system/memory_file' module FileSystemSupport def memory_fs(pretty_name, value, cannot_be_in_regex = nil) if !value.is_a?(Hash) raise "memory_fs() must take a Hash" end dir = Chef::ChefFS::FileSystem::MemoryRoot.new(pretty_name, cannot_be_in_regex) value.each do |key, child| dir.add_child(memory_fs_value(child, key.to_s, dir)) end dir end def memory_fs_value(value, name = '', parent = nil) if value.is_a?(Hash) dir = Chef::ChefFS::FileSystem::MemoryDir.new(name, parent) value.each do |key, child| dir.add_child(memory_fs_value(child, key.to_s, dir)) end dir else Chef::ChefFS::FileSystem::MemoryFile.new(name, parent, value || "#{name}\n") end end def pattern(p) Chef::ChefFS::FilePattern.new(p) end def return_paths(*expected) ReturnPaths.new(expected) end def no_blocking_calls_allowed [ Chef::ChefFS::FileSystem::MemoryFile, Chef::ChefFS::FileSystem::MemoryDir ].each do |c| [ :children, :exists?, :read ].each do |m| allow_any_instance_of(c).to receive(m).and_raise("#{m.to_s} should not be called") end end end def list_should_yield_paths(fs, pattern_str, *expected_paths) result_paths = [] Chef::ChefFS::FileSystem.list(fs, pattern(pattern_str)).each { |result| result_paths << result.path } expect(result_paths).to match_array(expected_paths) end end chef-12.3.0/spec/support/shared/unit/execute_resource.rb0000644000004100000410000000741012520074675023335 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 expect(@resource).to be_a_kind_of(Chef::Resource) expect(@resource).to be_a_kind_of(Chef::Resource::Execute) end it "should set the command to the first argument to new" do expect(@resource.command).to eql(resource_instance_name) end it "should accept an array on instantiation, too" do resource = Chef::Resource::Execute.new(%w{something else}) expect(resource).to be_a_kind_of(Chef::Resource) expect(resource).to be_a_kind_of(Chef::Resource::Execute) expect(resource.command).to eql(%w{something else}) end it "should accept a string for the command to run" do @resource.command "something" expect(@resource.command).to eql("something") end it "should accept an array for the command to run" do @resource.command %w{something else} expect(@resource.command).to eql(%w{something else}) end it "should accept a string for the cwd" do @resource.cwd "something" expect(@resource.cwd).to eql("something") end it "should accept a hash for the environment" do test_hash = { :one => :two } @resource.environment(test_hash) expect(@resource.environment).to eql(test_hash) end it "allows the environment to be specified with #env" do expect(@resource).to respond_to(:env) end it "should accept a string for the group" do @resource.group "something" expect(@resource.group).to eql("something") end it "should accept an integer for the group" do @resource.group 1 expect(@resource.group).to eql(1) end it "should accept an array for the execution path in Chef-12 and log deprecation message", :chef_lt_13_only do expect(Chef::Log).to receive(:warn).at_least(:once) @resource.path ["woot"] expect(@resource.path).to eql(["woot"]) end it "should raise an exception in chef-13", :chef_gte_13_only do expect(@resource.path [ "woot" ]).to raise_error end it "should accept an integer for the return code" do @resource.returns 1 expect(@resource.returns).to eql(1) end it "should accept an integer for the timeout" do @resource.timeout 1 expect(@resource.timeout).to eql(1) end it "should accept a string for the user" do @resource.user "something" expect(@resource.user).to eql("something") end it "should accept an integer for the user" do @resource.user 1 expect(@resource.user).to eql(1) end it "should accept a string for creates" do @resource.creates "something" expect(@resource.creates).to 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.returns(1) @resource.user("root") end it "returns the command as its identity" do expect(@resource.identity).to eq("grep") end end end chef-12.3.0/spec/support/shared/shared_examples.rb0000644000004100000410000000066612520074675022157 0ustar www-datawww-data# For storing any examples shared between multiple tests # Any object which defines a .to_json should import this test shared_examples "to_json equalivent to Chef::JSONCompat.to_json" do let(:jsonable) { raise "You must define the subject when including this test" } it "should allow consumers to call #to_json or Chef::JSONCompat.to_json" do expect(jsonable.to_json).to eq(Chef::JSONCompat.to_json(jsonable)) end end chef-12.3.0/spec/support/shared/functional/0000755000004100000410000000000012520074675020620 5ustar www-datawww-datachef-12.3.0/spec/support/shared/functional/file_resource.rb0000644000004100000410000007423512520074675024006 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # shared_context "deploying with move" do before do Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH Chef::Config[:file_atomic_update] = true end end shared_context "deploying with copy" do before do Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH Chef::Config[:file_atomic_update] = false end end shared_context "deploying via tmpdir" do before do Chef::Config[:file_staging_uses_destdir] = false Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH end end shared_context "deploying via destdir" do before do Chef::Config[:file_staging_uses_destdir] = true Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH end end shared_examples_for "a file with the wrong content" do before do # Assert starting state is as expected expect(File).to exist(path) # Kinda weird, in this case @expected_checksum is the cksum of the file # with incorrect content. expect(sha256_checksum(path)).to eq(@expected_checksum) end describe "when diff is disabled" do include_context "diff disabled" context "when running action :create" do context "with backups enabled" do before do resource.run_action(:create) end it "overwrites the file with the updated content when the :create action is run" do expect(File.stat(path).mtime).to be > @expected_mtime expect(sha256_checksum(path)).not_to eq(@expected_checksum) end it "backs up the existing file" do expect(Dir.glob(backup_glob).size).to equal(1) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end context "with backups disabled" do before do resource.backup(0) resource.run_action(:create) end it "should not attempt to backup the existing file if :backup == 0" do expect(Dir.glob(backup_glob).size).to equal(0) end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end context "with a checksum that does not match the content to deploy" do before do resource.checksum("aAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaA") end it "raises an exception" do expect{ resource.run_action(:create) }.to raise_error(Chef::Exceptions::ChecksumMismatch) end end end describe "when running action :create_if_missing" do before do resource.run_action(:create_if_missing) end it "doesn't overwrite the file when the :create_if_missing action is run" do expect(File.stat(path).mtime).to eq(@expected_mtime) expect(sha256_checksum(path)).to eq(@expected_checksum) end it "is not marked as updated" do expect(resource).not_to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end describe "when running action :delete" do before do resource.run_action(:delete) end it "deletes the file" do expect(File).not_to exist(path) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end end context "when diff is enabled" do describe 'sensitive attribute' do context "should be insensitive by default" do it { expect(resource.sensitive).to(be_falsey) } end context "when set" do before { resource.sensitive(true) } it "should be set on the resource" do expect(resource.sensitive).to(be_truthy) end context "when running :create action" do let(:provider) { resource.provider_for_action(:create) } let(:reporter_messages) { provider.instance_variable_get("@converge_actions").actions[0][0] } before do provider.run_action end it "should suppress the diff" do expect(resource.diff).to(include('suppressed sensitive resource')) expect(reporter_messages[1]).to eq("suppressed sensitive resource") end it "should still include the updated checksums" do expect(reporter_messages[0]).to include("update content in file") end end end end end end shared_examples_for "a file with the correct content" do before do # Assert starting state is as expected expect(File).to exist(path) expect(sha256_checksum(path)).to eq(@expected_checksum) end include_context "diff disabled" describe "when running action :create" do before do resource.run_action(:create) end it "does not overwrite the original when the :create action is run" do expect(sha256_checksum(path)).to eq(@expected_checksum) end it "does not update the mtime of the file when the :create action is run" do expect(File.stat(path).mtime).to eq(@expected_mtime) end it "is not marked as updated by last action" do expect(resource).not_to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end describe "when running action :create_if_missing" do before do resource.run_action(:create_if_missing) end it "doesn't overwrite the file when the :create_if_missing action is run" do expect(sha256_checksum(path)).to eq(@expected_checksum) end it "is not marked as updated by last action" do expect(resource).not_to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end describe "when running action :delete" do before do resource.run_action(:delete) end it "deletes the file when the :delete action is run" do expect(File).not_to exist(path) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end end shared_examples_for "a file resource" do describe "when deploying with :move" do include_context "deploying with move" describe "when deploying via tmpdir" do include_context "deploying via tmpdir" it_behaves_like "a configured file resource" end describe "when deploying via destdir" do include_context "deploying via destdir" it_behaves_like "a configured file resource" end end describe "when deploying with :copy" do include_context "deploying with copy" describe "when deploying via tmpdir" do include_context "deploying via tmpdir" it_behaves_like "a configured file resource" end describe "when deploying via destdir" do include_context "deploying via destdir" it_behaves_like "a configured file resource" end end describe "when running under why run" do before do Chef::Config[:why_run] = true Chef::Config[:ssl_verify_mode] = :verify_none end after do Chef::Config[:why_run] = false end context "and the resource has a path with a missing intermediate directory" do # CHEF-3978 let(:path) do File.join(test_file_dir, "intermediate_dir", make_tmpname(file_base)) end it "successfully doesn't create the file" do resource.run_action(:create) # should not raise expect(File).not_to exist(path) end end end describe "when setting atomic_update" do it "booleans should work" do expect {resource.atomic_update(true)}.not_to raise_error expect {resource.atomic_update(false)}.not_to raise_error end it "anything else should raise an error" do expect {resource.atomic_update(:copy)}.to raise_error(ArgumentError) expect {resource.atomic_update(:move)}.to raise_error(ArgumentError) expect {resource.atomic_update(958)}.to raise_error(ArgumentError) end end end shared_examples_for "file resource not pointing to a real file" do def symlink?(file_path) if windows? Chef::ReservedNames::Win32::File.symlink?(file_path) else File.symlink?(file_path) end end def real_file?(file_path) !symlink?(file_path) && File.file?(file_path) end before do Chef::Config[:ssl_verify_mode] = :verify_none end describe "when force_unlink is set to true" do it ":create unlinks the target" do expect(real_file?(path)).to be_falsey resource.force_unlink(true) resource.run_action(:create) expect(real_file?(path)).to be_truthy expect(binread(path)).to eq(expected_content) expect(resource).to be_updated_by_last_action end end describe "when force_unlink is set to false" do it ":create raises an error" do expect {resource.run_action(:create) }.to raise_error(Chef::Exceptions::FileTypeMismatch) end end describe "when force_unlink is not set (default)" do it ":create raises an error" do expect {resource.run_action(:create) }.to raise_error(Chef::Exceptions::FileTypeMismatch) end end end shared_examples_for "a configured file resource" do include_context "diff disabled" before do Chef::Log.level = :info Chef::Config[:ssl_verify_mode] = :verify_none end # note the stripping of the drive letter from the tmpdir on windows let(:backup_glob) { File.join(CHEF_SPEC_BACKUP_PATH, test_file_dir.sub(/^([A-Za-z]:)/, ""), "#{file_base}*") } # Most tests update the resource, but a few do not. We need to test that the # resource is marked updated or not correctly, but the test contexts are # composed between correct/incorrect content and correct/incorrect # permissions. We override this "let" definition in the context where content # and permissions are correct. let(:expect_updated?) { true } include Chef::Mixin::ShellOut def selinux_security_context_restored?(path) @restorecon_path = which("restorecon") if @restorecon_path.nil? restorecon_test_command = "#{@restorecon_path} -n -v #{path}" cmdresult = shell_out(restorecon_test_command) # restorecon will print the required changes to stdout if any is # needed cmdresult.stdout.empty? end def binread(file) content = File.open(file, "rb") do |f| f.read end content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding) content end context "when the target file is a symlink", :not_supported_on_win2k3 do let(:symlink_target) { File.join(CHEF_SPEC_DATA, "file-test-target") } describe "when configured not to manage symlink's target" do before(:each) do # configure not to manage symlink source resource.manage_symlink_source(false) # create symlinks for test context FileUtils.touch(symlink_target) if windows? Chef::ReservedNames::Win32::File.symlink(symlink_target, path) else File.symlink(symlink_target, path) end end after(:each) do FileUtils.rm_rf(symlink_target) FileUtils.rm_rf(path) end describe "when symlink target has correct content" do before(:each) do File.open(symlink_target, "wb") { |f| f.print expected_content } end it_behaves_like "file resource not pointing to a real file" end describe "when symlink target has the wrong content" do before(:each) do File.open(symlink_target, "wb") { |f| f.print "This is so wrong!!!" } end after(:each) do # symlink should never be followed expect(binread(symlink_target)).to eq("This is so wrong!!!") end it_behaves_like "file resource not pointing to a real file" end end # Unix-only for now. Windows behavior may differ because of how ACL # management handles symlinks. Since symlinks are rare on Windows and this # feature primarily exists to support the case where a well-known file # (e.g., resolv.conf) has been converted to a symlink, we're okay with the # discrepancy. context "when configured to manage the symlink source", :unix_only do before do resource.manage_symlink_source(true) end context "but the symlink is part of a loop" do let(:link1_path) { File.join(CHEF_SPEC_DATA, "points-to-link2") } let(:link2_path) { File.join(CHEF_SPEC_DATA, "points-to-link1") } before do # point resource at link1: resource.path(link1_path) # create symlinks for test context File.symlink(link1_path, link2_path) File.symlink(link2_path, link1_path) end after(:each) do FileUtils.rm_rf(link1_path) FileUtils.rm_rf(link2_path) end it "raises an InvalidSymlink error" do expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::InvalidSymlink) end it "issues a warning/assumption in whyrun mode" do begin Chef::Config[:why_run] = true resource.run_action(:create) # should not raise ensure Chef::Config[:why_run] = false end end end context "but the symlink points to a nonexistent file" do let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-nothing") } let(:not_existent_source) { File.join(CHEF_SPEC_DATA, "i-am-not-here") } before do resource.path(link_path) # create symlinks for test context File.symlink(not_existent_source, link_path) FileUtils.rm_rf(not_existent_source) end after(:each) do FileUtils.rm_rf(link_path) end it "raises an InvalidSymlink error" do expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::InvalidSymlink) end it "issues a warning/assumption in whyrun mode" do begin Chef::Config[:why_run] = true resource.run_action(:create) # should not raise ensure Chef::Config[:why_run] = false end end end context "but the symlink is points to a non-file fs entry" do let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-dir") } let(:not_a_file_path) { File.join(CHEF_SPEC_DATA, "dir-at-end-of-symlink") } before do # point resource at link1: resource.path(link_path) # create symlinks for test context File.symlink(not_a_file_path, link_path) Dir.mkdir(not_a_file_path) end after(:each) do FileUtils.rm_rf(link_path) FileUtils.rm_rf(not_a_file_path) end it "raises an InvalidSymlink error" do expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::FileTypeMismatch) end it "issues a warning/assumption in whyrun mode" do begin Chef::Config[:why_run] = true resource.run_action(:create) # should not raise ensure Chef::Config[:why_run] = false end end end context "when the symlink source is a real file" do let(:wrong_content) { "this is the wrong content" } let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-real-file") } before do # point resource at link: resource.path(link_path) # create symlinks for test context File.symlink(path, link_path) end after(:each) do # shared examples should not change our test setup of a file resource # pointing at a symlink: expect(resource.path).to eq(link_path) FileUtils.rm_rf(link_path) end context "and the permissions are incorrect" do before do # Create source (real) file File.open(path, "wb") { |f| f.write(expected_content) } end include_context "setup broken permissions" include_examples "a securable resource with existing target" it "does not replace the symlink with a real file" do resource.run_action(:create) expect(File).to be_symlink(link_path) end end context "and the content is incorrect" do before do # Create source (real) file File.open(path, "wb") { |f| f.write(wrong_content) } end it "updates the source file content" do skip end it "marks the resource as updated" do resource.run_action(:create) expect(resource).to be_updated_by_last_action end it "does not replace the symlink with a real file" do resource.run_action(:create) expect(File).to be_symlink(link_path) end end context "and the content and permissions are correct" do let(:expect_updated?) { false } before do # Create source (real) file File.open(path, "wb") { |f| f.write(expected_content) } end include_context "setup correct permissions" include_examples "a securable resource with existing target" end end context "when the symlink points to a symlink which points to a real file" do let(:wrong_content) { "this is the wrong content" } let(:link_to_file_path) { File.join(CHEF_SPEC_DATA, "points-to-real-file") } let(:link_to_link_path) { File.join(CHEF_SPEC_DATA, "points-to-next-link") } before do # point resource at link: resource.path(link_to_link_path) # create symlinks for test context File.symlink(path, link_to_file_path) File.symlink(link_to_file_path, link_to_link_path) # Create source (real) file File.open(path, "wb") { |f| f.write(wrong_content) } end include_context "setup broken permissions" include_examples "a securable resource with existing target" after(:each) do # shared examples should not change our test setup of a file resource # pointing at a symlink: expect(resource.path).to eq(link_to_link_path) FileUtils.rm_rf(link_to_file_path) FileUtils.rm_rf(link_to_link_path) end it "does not replace the symlink with a real file" do resource.run_action(:create) expect(File).to be_symlink(link_to_link_path) expect(File).to be_symlink(link_to_file_path) end end end end context "when the target file does not exist" do before(:each) do FileUtils.rm_rf(path) end after(:each) do FileUtils.rm_rf(path) end def symlink?(file_path) if windows? Chef::ReservedNames::Win32::File.symlink?(file_path) else File.symlink?(file_path) end end def real_file?(file_path) !symlink?(file_path) && File.file?(file_path) end describe "when force_unlink is set to true" do it ":create updates the target" do resource.force_unlink(true) resource.run_action(:create) expect(real_file?(path)).to be_truthy expect(binread(path)).to eq(expected_content) expect(resource).to be_updated_by_last_action end end describe "when force_unlink is set to false" do it ":create updates the target" do resource.force_unlink(true) resource.run_action(:create) expect(real_file?(path)).to be_truthy expect(binread(path)).to eq(expected_content) expect(resource).to be_updated_by_last_action end end describe "when force_unlink is not set (default)" do it ":create updates the target" do resource.force_unlink(true) resource.run_action(:create) expect(real_file?(path)).to be_truthy expect(binread(path)).to eq(expected_content) expect(resource).to be_updated_by_last_action end end end context "when the target file is a directory" do before(:each) do FileUtils.mkdir_p(path) end after(:each) do FileUtils.rm_rf(path) end it_behaves_like "file resource not pointing to a real file" end context "when the target file is a blockdev",:unix_only, :requires_root, :not_supported_on_solaris do include Chef::Mixin::ShellOut let(:path) do File.join(CHEF_SPEC_DATA, "testdev") end before(:each) do result = shell_out("mknod #{path} b 1 2") result.stderr.empty? end after(:each) do FileUtils.rm_rf(path) end it_behaves_like "file resource not pointing to a real file" end context "when the target file is a chardev",:unix_only, :requires_root, :not_supported_on_solaris do include Chef::Mixin::ShellOut let(:path) do File.join(CHEF_SPEC_DATA, "testdev") end before(:each) do result = shell_out("mknod #{path} c 1 2") result.stderr.empty? end after(:each) do FileUtils.rm_rf(path) end it_behaves_like "file resource not pointing to a real file" end context "when the target file is a pipe",:unix_only do include Chef::Mixin::ShellOut let(:path) do File.join(CHEF_SPEC_DATA, "testpipe") end before(:each) do result = shell_out("mkfifo #{path}") result.stderr.empty? end after(:each) do FileUtils.rm_rf(path) end it_behaves_like "file resource not pointing to a real file" end context "when the target file is a socket",:unix_only do require 'socket' # It turns out that the path to a socket can have at most ~104 # bytes. Therefore we are creating our sockets in tmpdir so that # they have a shorter path. let(:test_socket_dir) { File.join(Dir.tmpdir, "sockets") } before do FileUtils::mkdir_p(test_socket_dir) end after do FileUtils::rm_rf(test_socket_dir) end let(:path) do File.join(test_socket_dir, "testsocket") end before(:each) do expect(path.bytesize).to be <= 104 UNIXServer.new(path) end after(:each) do FileUtils.rm_rf(path) end it_behaves_like "file resource not pointing to a real file" end # Regression test for http://tickets.opscode.com/browse/CHEF-4082 context "when notification is configured" do describe "when path is specified with normal separator" do before do @notified_resource = Chef::Resource.new("punk", resource.run_context) resource.notifies(:run, @notified_resource, :immediately) resource.run_action(:create) end it "should notify the other resources correctly" do expect(resource).to be_updated_by_last_action expect(resource.run_context.immediate_notifications(resource).length).to eq(1) end end describe "when path is specified with windows separator", :windows_only do let(:path) { File.join(test_file_dir, make_tmpname(file_base)).gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR) } before do @notified_resource = Chef::Resource.new("punk", resource.run_context) resource.notifies(:run, @notified_resource, :immediately) resource.run_action(:create) end it "should notify the other resources correctly" do expect(resource).to be_updated_by_last_action expect(resource.run_context.immediate_notifications(resource).length).to eq(1) end end end context "when the target file does not exist" do before do # Assert starting state is expected expect(File).not_to exist(path) end describe "when running action :create" do before do resource.run_action(:create) end it "creates the file when the :create action is run" do expect(File).to exist(path) end it "creates the file with the correct content when the :create action is run" do expect(binread(path)).to eq(expected_content) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end describe "when running action :create_if_missing" do before do resource.run_action(:create_if_missing) end it "creates the file with the correct content" do expect(binread(path)).to eq(expected_content) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end it "should restore the security contexts on selinux", :selinux_only do expect(selinux_security_context_restored?(path)).to be_truthy end end describe "when running action :delete" do before do resource.run_action(:delete) end it "deletes the file when the :delete action is run" do expect(File).not_to exist(path) end it "is not marked updated by last action" do expect(resource).not_to be_updated_by_last_action end end end # Set up the context for security tests def allowed_acl(sid, expected_perms) [ ACE.access_allowed(sid, expected_perms[:specific]) ] end def denied_acl(sid, expected_perms) [ ACE.access_denied(sid, expected_perms[:specific]) ] end def parent_inheritable_acls dummy_file_path = File.join(test_file_dir, "dummy_file") FileUtils.touch(dummy_file_path) dummy_desc = get_security_descriptor(dummy_file_path) FileUtils.rm_rf(dummy_file_path) dummy_desc end it_behaves_like "a securable resource without existing target" context "when the target file has the wrong content" do before(:each) do File.open(path, "wb") { |f| f.print "This is so wrong!!!" } now = Time.now.to_i File.utime(now - 9000, now - 9000, path) @expected_mtime = File.stat(path).mtime @expected_checksum = sha256_checksum(path) end describe "and the target file has the correct permissions" do include_context "setup correct permissions" it_behaves_like "a file with the wrong content" it_behaves_like "a securable resource with existing target" end context "and the target file has incorrect permissions" do include_context "setup broken permissions" it_behaves_like "a file with the wrong content" it_behaves_like "a securable resource with existing target" end end context "when the target file has the correct content" do before(:each) do File.open(path, "wb") { |f| f.print expected_content } now = Time.now.to_i File.utime(now - 9000, now - 9000, path) @expected_mtime = File.stat(path).mtime @expected_checksum = sha256_checksum(path) end describe "and the target file has the correct permissions" do # When permissions and content are correct, chef should do nothing and # the resource should not be marked updated. let(:expect_updated?) { false } include_context "setup correct permissions" it_behaves_like "a file with the correct content" it_behaves_like "a securable resource with existing target" end context "and the target file has incorrect permissions" do include_context "setup broken permissions" it_behaves_like "a file with the correct content" it_behaves_like "a securable resource with existing target" end end # Regression test for http://tickets.opscode.com/browse/CHEF-4419 context "when the path starts with '/' and target file exists", :windows_only do let(:path) do File.join(test_file_dir[2..test_file_dir.length], make_tmpname(file_base)) end before do File.open(path, "wb") { |f| f.print expected_content } now = Time.now.to_i File.utime(now - 9000, now - 9000, path) @expected_mtime = File.stat(path).mtime @expected_checksum = sha256_checksum(path) end describe ":create action should run without any updates" do before do # Assert starting state is as expected expect(File).to exist(path) expect(sha256_checksum(path)).to eq(@expected_checksum) resource.run_action(:create) end it "does not overwrite the original when the :create action is run" do expect(sha256_checksum(path)).to eq(@expected_checksum) end it "does not update the mtime of the file when the :create action is run" do expect(File.stat(path).mtime).to eq(@expected_mtime) end it "is not marked as updated by last action" do expect(resource).not_to be_updated_by_last_action end end end end shared_context Chef::Resource::File do if windows? require 'chef/win32/file' end # We create the files in a different directory than tmp to exercise # different file deployment strategies more completely. let(:test_file_dir) do if windows? File.join(ENV['systemdrive'], "test-dir") else File.join(CHEF_SPEC_DATA, "test-dir") end end let(:path) do File.join(test_file_dir, make_tmpname(file_base)) end before do FileUtils::mkdir_p(test_file_dir) end after(:each) do FileUtils.rm_r(path) if File.exists?(path) FileUtils.rm_r(CHEF_SPEC_BACKUP_PATH) if File.exists?(CHEF_SPEC_BACKUP_PATH) end after do FileUtils::rm_rf(test_file_dir) end end chef-12.3.0/spec/support/shared/functional/win32_service.rb0000644000004100000410000000306412520074675023632 0ustar www-datawww-data require 'chef/application/windows_service_manager' shared_context "using Win32::Service" do # Some helper methods. def test_service_exists? ::Win32::Service.exists?("spec-service") end def test_service_state ::Win32::Service.status("spec-service").current_state end def service_manager Chef::Application::WindowsServiceManager.new(test_service) end def cleanup # Uninstall if the test service is installed. if test_service_exists? # We can only uninstall when the service is stopped. if test_service_state != "stopped" ::Win32::Service.send("stop", "spec-service") while test_service_state != "stopped" sleep 1 end end ::Win32::Service.delete("spec-service") end # Delete the test_service_file if it exists if File.exists?(test_service_file) File.delete(test_service_file) end end # Definition for the test-service let(:test_service) { { :service_name => "spec-service", :service_display_name => "Spec Test Service", :service_description => "Service for testing Chef::Application::WindowsServiceManager.", :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb')) } } # Test service creates a file for us to verify that it is running. # Since our test service is running as Local System we should look # for the file it creates under SYSTEM temp directory let(:test_service_file) { "#{ENV['SystemDrive']}\\windows\\temp\\spec_service_file" } end chef-12.3.0/spec/support/shared/functional/windows_script.rb0000644000004100000410000001274312520074675024232 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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. # # Shared context used by both Powershell and Batch script provider # tests. shared_context Chef::Resource::WindowsScript do before(:all) do ohai_reader = Ohai::System.new ohai_reader.all_plugins("platform") new_node = Chef::Node.new new_node.consume_external_attrs(ohai_reader.data,{}) events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(new_node, {}, events) end let(:script_output_path) do File.join(Dir.tmpdir, make_tmpname("windows_script_test")) end before(:each) do 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 let!(:resource) do Chef::Resource::WindowsScript::Batch.new("Batch resource functional test", @run_context) end shared_examples_for "a script resource with architecture attribute" do context "with the given architecture attribute value" do let(:resource_architecture) { architecture } let(:expected_architecture) do if architecture expected_architecture = architecture else expected_architecture = :i386 end end let(:expected_architecture_output) do expected_architecture == :i386 ? 'X86' : 'AMD64' end let(:guard_script_suffix) do "guard" end let(:guard_script_output_path) do "#{script_output_path}#{guard_script_suffix}" end let(:resource_command) do "#{architecture_command} #{output_command} #{script_output_path}" end let(:resource_guard_command) do "#{architecture_command} #{output_command} #{guard_script_output_path}" end before(:each) do resource.code resource_command (resource.architecture architecture) if architecture resource.returns(0) end it "should create a process with the expected architecture" do resource.run_action(:run) expect(get_process_architecture).to eq(expected_architecture_output.downcase) end it "should execute guards with the same architecture as the resource" do resource.only_if resource_guard_command resource.run_action(:run) expect(get_process_architecture).to eq(expected_architecture_output.downcase) expect(get_guard_process_architecture).to eq(expected_architecture_output.downcase) expect(get_guard_process_architecture).to eq(get_process_architecture) end let (:architecture) { :x86_64 } it "should execute a 64-bit guard if the guard's architecture is specified as 64-bit", :windows64_only do resource.only_if resource_guard_command, :architecture => :x86_64 resource.run_action(:run) expect(get_guard_process_architecture).to eq('amd64') end let (:architecture) { :i386 } it "should execute a 32-bit guard if the guard's architecture is specified as 32-bit" do resource.only_if resource_guard_command, :architecture => :i386 resource.run_action(:run) expect(get_guard_process_architecture).to eq('x86') end end end shared_examples_for "a Windows script running on Windows" do describe "when the run action is invoked on Windows" do it "executes the script code" do resource.code("@whoami > #{script_output_path}") resource.returns(0) resource.run_action(:run) end end context "when evaluating guards" do it "has a guard_interpreter attribute set to the short name of the resource" do expect(resource.guard_interpreter).to eq(resource.resource_name) resource.not_if "findstr.exe /thiscommandhasnonzeroexitstatus" expect(Chef::Resource).to receive(:resource_for_node).and_call_original expect(resource.class).to receive(:new).and_call_original expect(resource.should_skip?(:run)).to be_falsey end end context "when the architecture attribute is not set" do let(:architecture) { nil } it_behaves_like "a script resource with architecture attribute" end context "when the architecture attribute is :i386" do let(:architecture) { :i386 } it_behaves_like "a script resource with architecture attribute" end context "when the architecture attribute is :x86_64" do let(:architecture) { :x86_64 } it_behaves_like "a script resource with architecture attribute" end end def get_windows_script_output(suffix = '') File.read("#{script_output_path}#{suffix}") end def source_contains_case_insensitive_content?( source, content ) source.downcase.include?(content.downcase) end def get_guard_process_architecture get_process_architecture(guard_script_suffix) end def get_process_architecture(suffix = '') get_windows_script_output(suffix).strip.downcase end end chef-12.3.0/spec/support/shared/functional/securable_resource_with_reporting.rb0000644000004100000410000003112712520074675030151 0ustar www-datawww-data require 'functional/resource/base' ALL_EXPANDED_PERMISSIONS = ["generic read", "generic write", "generic execute", "generic all", "delete", "read permissions", "change permissions", "take ownership", "synchronize", "access system security", "read data / list directory", "write data / add file", "append data / add subdirectory", "read extended attributes", "write extended attributes", "execute / traverse", "delete child", "read attributes", "write attributes"] shared_examples_for "a securable resource with reporting" do include_context "diff disabled" let(:current_resource) do provider = resource.provider_for_action(resource.action) provider.load_current_resource provider.current_resource end # Default mode varies based on implementation. Providers that use a tempfile # will default to 0600. Providers that use File.open will default to 0666 - # umask # let(:default_mode) { (0666 & ~File.umask).to_s(8) } describe "reading file security metadata for reporting on unix", :unix_only => true do # According to POSIX standard created files get either the # effective gid of the process or inherits the gid of the parent # directory based on file system. Since it's hard to guess what # would happen on each platform we create a dummy file and see # what the group name should be. before do FileUtils.touch(path) @expected_gid = File.stat(path).gid @expected_group_name = Etc.getgrgid(@expected_gid).name FileUtils.rm_rf(path) end context "when the target file doesn't exist" do before do resource.action(:create) end it "has empty values for file metadata in 'current_resource'" do expect(current_resource.owner).to be_nil expect(current_resource.group).to be_nil expect(current_resource.mode).to be_nil end context "and no security metadata is specified in new_resource" do it "sets the metadata values on the new_resource as strings after creating" do resource.run_action(:create) # TODO: most stable way to specify? expect(resource.owner).to eq(Etc.getpwuid(Process.uid).name) expect(resource.group).to eq(@expected_group_name) expect(resource.mode).to eq("0#{default_mode}") end end context "and owner is specified with a String (username) in new_resource", :requires_root => true do # TODO/bug: duplicated from the "securable resource" tests if ohai[:platform] == "aix" let(:expected_user_name) { 'guest' } else let(:expected_user_name) { 'nobody' } end before do resource.owner(expected_user_name) resource.run_action(:create) end it "sets the owner on new_resource to the username (String) of the desired owner" do expect(resource.owner).to eq(expected_user_name) end end context "and owner is specified with an Integer (uid) in new_resource", :requires_root => true do # TODO: duplicated from "securable resource" if ohai[:platform] == "aix" let(:expected_user_name) { 'guest' } else let(:expected_user_name) { 'nobody' } end let(:expected_uid) { Etc.getpwnam(expected_user_name).uid } let(:desired_gid) { 1337 } let(:expected_gid) { 1337 } before do resource.owner(expected_uid) resource.run_action(:create) end it "sets the owner on new_resource to the uid (Integer) of the desired owner" do expect(resource.owner).to eq(expected_uid) end end context "and group is specified with a String (group name)", :requires_root => true do let(:expected_group_name) { Etc.getgrent.name } before do resource.group(expected_group_name) resource.run_action(:create) end it "sets the group on new_resource to the group name (String) of the group" do expect(resource.group).to eq(expected_group_name) end end context "and group is specified with an Integer (gid)", :requires_root => true do let(:expected_gid) { Etc.getgrent.gid } before do resource.group(expected_gid) resource.run_action(:create) end it "sets the group on new_resource to the gid (Integer)" do expect(resource.group).to eq(expected_gid) end end context "and mode is specified as a String" do # Need full permission for owner here or else remote directory gets # into trouble trying to manage nested directories let(:set_mode) { "0740" } let(:expected_mode) { "0740" } before do resource.mode(set_mode) resource.run_action(:create) end it "sets mode on the new_resource as a String" do expect(resource.mode).to eq(expected_mode) end end context "and mode is specified as an Integer" do let(:set_mode) { 00740 } let(:expected_mode) { "0740" } before do resource.mode(set_mode) resource.run_action(:create) end it "sets mode on the new resource as a String" do expect(resource.mode).to eq(expected_mode) end end end context "when the target file exists" do before do FileUtils.touch(resource.path) resource.action(:create) end context "and no security metadata is specified in new_resource" do it "sets the current values on current resource as strings" do # TODO: most stable way to specify? expect(current_resource.owner).to eq(Etc.getpwuid(Process.uid).name) expect(current_resource.group).to eq(@expected_group_name) expect(current_resource.mode).to eq("0#{(0666 & ~File.umask).to_s(8)}") end end context "and owner is specified with a String (username) in new_resource" do let(:expected_user_name) { Etc.getpwuid(Process.uid).name } before do resource.owner(expected_user_name) end it "sets the owner on new_resource to the username (String) of the desired owner" do expect(current_resource.owner).to eq(expected_user_name) end end context "and owner is specified with an Integer (uid) in new_resource" do let(:expected_uid) { Process.uid } before do resource.owner(expected_uid) end it "sets the owner on new_resource to the uid (Integer) of the desired owner" do expect(current_resource.owner).to eq(expected_uid) end end context "and group is specified with a String (group name)" do before do resource.group(@expected_group_name) end it "sets the group on new_resource to the group name (String) of the group" do expect(current_resource.group).to eq(@expected_group_name) end end context "and group is specified with an Integer (gid)" do before do resource.group(@expected_gid) end it "sets the group on new_resource to the gid (Integer)" do expect(current_resource.group).to eq(@expected_gid) end end context "and mode is specified as a String" do let(:default_create_mode) { 0666 & ~File.umask } let(:expected_mode) { "0#{default_create_mode.to_s(8)}" } before do resource.mode(expected_mode) end it "sets mode on the new_resource as a String" do expect(current_resource.mode).to eq(expected_mode) end end context "and mode is specified as an Integer" do let(:set_mode) { 0666 & ~File.umask } let(:expected_mode) { "0#{set_mode.to_s(8)}" } before do resource.mode(set_mode) end it "sets mode on the new resource as a String" do expect(current_resource.mode).to eq(expected_mode) end end end end describe "reading file security metadata for reporting on windows", :windows_only do context "when the target file doesn't exist" do # Windows reporting data should look like this (+/- ish): # { "owner" => "bob", "checksum" => "ffff", "access control" => { "bob" => { "permissions" => ["perm1", "perm2", ...], "flags" => [] }}} before do resource.action(:create) end it "has empty values for file metadata in 'current_resource'" do pending "windows reporting not yet fully supported" expect(current_resource.owner).to be_nil expect(current_resource.expanded_rights).to be_nil end context "and no security metadata is specified in new_resource" do before do pending "windows reporting not yet fully supported" end it "sets the metadata values on the new_resource as strings after creating" do resource.run_action(:create) # TODO: most stable way to specify? expect(resource.owner).to eq(etc.getpwuid(process.uid).name) expect(resource.state[:expanded_rights]).to eq({ "CURRENTUSER" => { "permissions" => ALL_EXPANDED_PERMISSIONS, "flags" => [] }}) expect(resource.state[:expanded_deny_rights]).to eq({}) expect(resource.state[:inherits]).to be_truthy end end context "and owner is specified with a string (username) in new_resource" do # TODO/bug: duplicated from the "securable resource" tests let(:expected_user_name) { 'Guest' } before do resource.owner(expected_user_name) resource.run_action(:create) end it "sets the owner on new_resource to the username (string) of the desired owner" do expect(resource.owner).to eq(expected_user_name) end end context "and owner is specified with a fully qualified domain user" do # TODO: duplicated from "securable resource" let(:expected_user_name) { 'domain\user' } before do pending "windows reporting not yet fully supported" resource.owner(expected_user_name) resource.run_action(:create) end it "sets the owner on new_resource to the fully qualified name of the desired owner" do expect(resource.owner).to eq(expected_user_name) end end end context "when the target file exists" do before do pending "windows reporting not yet fully supported" FileUtils.touch(resource.path) resource.action(:create) end context "and no security metadata is specified in new_resource" do it "sets the current values on current resource as strings" do # TODO: most stable way to specify? expect(current_resource.owner).to eq(etc.getpwuid(process.uid).name) expect(current_resource.expanded_rights).to eq({ "CURRENTUSER" => ALL_EXPANDED_PERMISSIONS }) end end context "and owner is specified with a string (username) in new_resource" do let(:expected_user_name) { etc.getpwuid(process.uid).name } before do resource.owner(expected_user_name) end it "sets the owner on current_resource to the username (string) of the desired owner" do expect(current_resource.owner).to eq(expected_user_name) end end context "and owner is specified as a fully qualified 'domain\\user' in new_resource" do let(:expected_user_name) { 'domain\user' } before do resource.owner(expected_user_name) end it "sets the owner on current_resource to the fully qualified name of the desired owner" do expect(current_resource.owner).to eq(expected_uid) end end context "and access rights are specified on the new_resource" do # TODO: before do blah it "sets the expanded_rights on the current resource" do skip end end context "and no access rights are specified on the current resource" do # TODO: before do blah it "sets the expanded rights on the current resource" do skip end end end end end chef-12.3.0/spec/support/shared/functional/securable_resource.rb0000644000004100000410000004346212520074675025032 0ustar www-datawww-data# # Author:: Seth Chisamore () # Author:: Mark Mzyk () # Author:: John Keiser () # 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 'etc' require 'functional/resource/base' shared_context "setup correct permissions" do if windows? include_context "use Windows permissions" end # I could not get this to work with :requires_unprivileged_user for whatever # reason. The setup when running as root is the same as non-root, except we # also do a chown, so this sets up correct context for either case. before :each, :unix_only do File.chmod(0776, path) now = Time.now.to_i File.utime(now - 9000, now - 9000, path) end # Root only context. before :each, :unix_only, :requires_root do if ohai[:platform] == "aix" File.chown(Etc.getpwnam('guest').uid, 1337, path) else File.chown(Etc.getpwnam('nobody').uid, 1337, path) end end before :each, :windows_only do so = SecurableObject.new(path) so.owner = SID.Administrator so.group = SID.Administrators dacl = ACL.create(denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Guest, expected_read_perms)) so.dacl = dacl end end shared_context "setup broken permissions" do if windows? include_context "use Windows permissions" end before :each, :unix_only do File.chmod(0644, path) end before :each, :unix_only, :requires_root do File.chown(0, 0, path) end before :each, :windows_only do so = SecurableObject.new(path) so.owner = SID.Guest so.group = SID.Everyone dacl = ACL.create(allowed_acl(SID.Guest, expected_modify_perms)) so.set_dacl(dacl, true) end end shared_context "use Windows permissions", :windows_only do if windows? SID ||= Chef::ReservedNames::Win32::Security::SID ACE ||= Chef::ReservedNames::Win32::Security::ACE ACL ||= Chef::ReservedNames::Win32::Security::ACL SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject end def get_security_descriptor(path) Chef::ReservedNames::Win32::Security.get_named_security_info(path) end def explicit_aces descriptor.dacl.select { |ace| ace.explicit? } end def extract_ace_properties(aces) hashes = [] aces.each do |ace| hashes << { :mask => ace.mask, :type => ace.type, :flags => ace.flags } end hashes end # Standard expected rights let(:expected_read_perms) do { :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ, :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ, } end let(:expected_read_execute_perms) do { :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE, :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE } end let(:expected_write_perms) do { :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE, :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE } end let(:expected_modify_perms) do { :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE, :specific => Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::DELETE } end let(:expected_full_control_perms) do { :generic => Chef::ReservedNames::Win32::API::Security::GENERIC_ALL, :specific => Chef::ReservedNames::Win32::API::Security::FILE_ALL_ACCESS } end RSpec::Matchers.define :have_expected_properties do |mask, type, flags| match do |ace| ace.mask == mask ace.type == type ace.flags == flags end end def descriptor get_security_descriptor(path) end end shared_examples_for "a securable resource with existing target" do include_context "diff disabled" context "on Unix", :unix_only do if ohai[:platform] == "aix" let(:expected_user_name) { 'guest' } else let(:expected_user_name) { 'nobody' } end let(:expected_uid) { Etc.getpwnam(expected_user_name).uid } let(:desired_gid) { 1337 } let(:expected_gid) { 1337 } skip "should set an owner (Rerun specs under root)", :requires_unprivileged_user => true skip "should set a group (Rerun specs under root)", :requires_unprivileged_user => true describe "when setting the owner", :requires_root do before do resource.owner expected_user_name resource.run_action(:create) end it "should set an owner" do expect(File.lstat(path).uid).to eq(expected_uid) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end describe "when setting the group", :requires_root do before do resource.group desired_gid resource.run_action(:create) end it "should set a group" do expect(File.lstat(path).gid).to eq(expected_gid) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end describe "when setting the permissions from octal given as a String" do before do @mode_string = '776' resource.mode @mode_string resource.run_action(:create) end it "should set permissions as specified" do pending("Linux does not support lchmod") expect{ File.lstat(path).mode & 007777 }.to eq(@mode_string.oct & 007777) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end describe "when setting permissions from a literal octal Integer" do before do @mode_integer = 0776 resource.mode @mode_integer resource.run_action(:create) end it "should set permissions in numeric form as a ruby-interpreted octal" do pending('Linux does not support lchmod') expect{ File.lstat(path).mode & 007777 }.to eq(@mode_integer & 007777) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end end context "on Windows", :windows_only do include_context "use Windows permissions" describe "when setting owner" do before do resource.owner(SID.admin_account_name) resource.run_action(:create) end it "should set the owner" do expect(descriptor.owner).to eq(SID.Administrator) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end describe "when setting group" do before do resource.group('Administrators') resource.run_action(:create) end it "should set the group" do expect(descriptor.group).to eq(SID.Administrators) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end describe "when setting rights and deny_rights" do before do resource.deny_rights(:modify, 'Guest') resource.rights(:read, 'Guest') resource.run_action(:create) end it "should set the rights and deny_rights" do expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Guest, expected_read_perms)) end it "is marked as updated only if changes are made" do expect(resource.updated_by_last_action?).to eq(expect_updated?) end end end end shared_examples_for "a securable resource without existing target" do include_context "diff disabled" context "on Unix", :unix_only do skip "if we need any securable resource tests on Unix without existing target resource." end context "on Windows", :windows_only do include_context "use Windows permissions" it "sets owner to Administrators on create if owner is not specified" do expect(File.exist?(path)).to eq(false) resource.run_action(:create) expect(descriptor.owner).to eq(SID.Administrators) end it "sets owner when owner is specified" do resource.owner 'Guest' resource.run_action(:create) expect(descriptor.owner).to eq(SID.Guest) end it "fails to set owner when owner has invalid characters" do expect { resource.owner 'Lance "The Nose" Glindenberry III' }.to raise_error#(Chef::Exceptions::ValidationFailed) end it "sets owner when owner is specified with a \\" do resource.owner "#{ENV['USERDOMAIN']}\\Guest" resource.run_action(:create) expect(descriptor.owner).to eq(SID.Guest) end it "leaves owner alone if owner is not specified and resource already exists" do # Set owner to Guest so it's not the same as the current user (which is the default on create) resource.owner 'Guest' resource.run_action(:create) expect(descriptor.owner).to eq(SID.Guest) new_resource = create_resource expect(new_resource.owner).to eq(nil) new_resource.run_action(:create) expect(descriptor.owner).to eq(SID.Guest) end it "sets group to None on create if group is not specified" do expect(resource.group).to eq(nil) expect(File.exist?(path)).to eq(false) resource.run_action(:create) expect(descriptor.group).to eq(SID.None) end it "sets group when group is specified" do resource.group 'Everyone' resource.run_action(:create) expect(descriptor.group).to eq(SID.Everyone) end it "fails to set group when group has invalid characters" do expect { resource.group 'Lance "The Nose" Glindenberry III' }.to raise_error(Chef::Exceptions::ValidationFailed) end it "sets group when group is specified with a \\" do pending("Need to find a group containing a backslash that is on most peoples' machines") resource.group "#{ENV['COMPUTERNAME']}\\Administrators" resource.run_action(:create) expect{ descriptor.group }.to eq(SID.Everyone) end it "leaves group alone if group is not specified and resource already exists" do # Set group to Everyone so it's not the default (None) resource.group 'Everyone' resource.run_action(:create) expect(descriptor.group).to eq(SID.Everyone) new_resource = create_resource expect(new_resource.group).to eq(nil) new_resource.run_action(:create) expect(descriptor.group).to eq(SID.Everyone) end describe "with rights and deny_rights attributes" do it "correctly sets :read rights" do resource.rights(:read, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms)) end it "correctly sets :read_execute rights" do resource.rights(:read_execute, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms)) end it "correctly sets :write rights" do resource.rights(:write, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms)) end it "correctly sets :modify rights" do resource.rights(:modify, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms)) end it "correctly sets :full_control rights" do resource.rights(:full_control, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms)) end it "correctly sets deny_rights" do # deny is an ACE with full rights, but is a deny type ace, not an allow type resource.deny_rights(:full_control, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms)) end it "Sets multiple rights" do resource.rights(:read, 'Everyone') resource.rights(:modify, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq( allowed_acl(SID.Everyone, expected_read_perms) + allowed_acl(SID.Guest, expected_modify_perms) ) end it "Sets deny_rights ahead of rights" do resource.rights(:read, 'Everyone') resource.deny_rights(:modify, 'Guest') resource.run_action(:create) expect(explicit_aces).to eq( denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Everyone, expected_read_perms) ) end it "Sets deny_rights ahead of rights when specified in reverse order" do resource.deny_rights(:modify, 'Guest') resource.rights(:read, 'Everyone') resource.run_action(:create) expect(explicit_aces).to eq( denied_acl(SID.Guest, expected_modify_perms) + allowed_acl(SID.Everyone, expected_read_perms) ) end end context "with a mode attribute" do if windows? Security ||= Chef::ReservedNames::Win32::API::Security end it "respects mode in string form as an octal number" do #on windows, mode cannot modify owner and/or group permissons #unless the owner and/or group as appropriate is specified resource.mode '400' resource.owner 'Guest' resource.group 'Everyone' resource.run_action(:create) expect(explicit_aces).to eq([ ACE.access_allowed(SID.Guest, Security::FILE_GENERIC_READ) ]) end it "respects mode in numeric form as a ruby-interpreted octal" do resource.mode 0700 resource.owner 'Guest' resource.run_action(:create) expect(explicit_aces).to eq([ ACE.access_allowed(SID.Guest, Security::FILE_GENERIC_READ | Security::FILE_GENERIC_WRITE | Security::FILE_GENERIC_EXECUTE | Security::DELETE) ]) end it "respects the owner, group and everyone bits of mode" do resource.mode 0754 resource.owner 'Guest' resource.group 'Administrators' resource.run_action(:create) expect(explicit_aces).to eq([ ACE.access_allowed(SID.Guest, Security::FILE_GENERIC_READ | Security::FILE_GENERIC_WRITE | Security::FILE_GENERIC_EXECUTE | Security::DELETE), ACE.access_allowed(SID.Administrators, Security::FILE_GENERIC_READ | Security::FILE_GENERIC_EXECUTE), ACE.access_allowed(SID.Everyone, Security::FILE_GENERIC_READ) ]) end it "respects the individual read, write and execute bits of mode" do resource.mode 0421 resource.owner 'Guest' resource.group 'Administrators' resource.run_action(:create) expect(explicit_aces).to eq([ ACE.access_allowed(SID.Guest, Security::FILE_GENERIC_READ), ACE.access_allowed(SID.Administrators, Security::FILE_GENERIC_WRITE | Security::DELETE), ACE.access_allowed(SID.Everyone, Security::FILE_GENERIC_EXECUTE) ]) end it 'warns when mode tries to set owner bits but owner is not specified' do @warn = [] allow(Chef::Log).to receive(:warn) { |msg| @warn << msg } resource.mode 0400 resource.run_action(:create) expect(@warn.include?("Mode 400 includes bits for the owner, but owner is not specified")).to be_truthy end it 'warns when mode tries to set group bits but group is not specified' do @warn = [] allow(Chef::Log).to receive(:warn) { |msg| @warn << msg } resource.mode 0040 resource.run_action(:create) expect(@warn.include?("Mode 040 includes bits for the group, but group is not specified")).to be_truthy end end it "does not inherit aces if inherits is set to false" do # We need at least one ACE if we're creating a securable without # inheritance resource.rights(:full_control, 'Administrators') resource.inherits(false) resource.run_action(:create) descriptor.dacl.each do | ace | expect(ace.inherited?).to eq(false) end end it "has the inheritable acls of parent directory if no acl is specified" do expect(File.exist?(path)).to eq(false) # Collect the inheritable acls form the parent by creating a file without # any specific ACLs parent_acls = parent_inheritable_acls # On certain flavors of Windows the default list of ACLs sometimes includes # non-inherited ACLs. Filter them out here. parent_inherited_acls = parent_acls.dacl.collect do |ace| ace.inherited? end resource.run_action(:create) # Similarly filter out the non-inherited ACLs resource_inherited_acls = descriptor.dacl.collect do |ace| ace.inherited? end expect(resource_inherited_acls).to eq(parent_inherited_acls) end end end chef-12.3.0/spec/support/shared/functional/http.rb0000644000004100000410000001736712520074675022142 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # shared code for Chef::REST and Chef::HTTP::Simple and other Chef::HTTP wrappers # module ChefHTTPShared def nyan_uncompressed_filename File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') end def nyan_compressed_filename File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz') end def binread(file) content = File.open(file, "rb") do |f| f.read end content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding) content end def start_tiny_server(server_opts={}) nyan_uncompressed_size = File::Stat.new(nyan_uncompressed_filename).size nyan_compressed_size = File::Stat.new(nyan_compressed_filename).size @server = TinyServer::Manager.new(server_opts) @server.start @api = TinyServer::API.instance @api.clear # # trivial endpoints # # just a normal file # (expected_content should be uncompressed) @api.get("/nyan_cat.png", 200) { File.open(nyan_uncompressed_filename, "rb") do |f| f.read end } # this ends in .gz, we do not uncompress it and drop it on the filesystem as a .gz file (the internet often lies) # (expected_content should be compressed) @api.get("/nyan_cat.png.gz", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { File.open(nyan_compressed_filename, "rb") do |f| f.read end } # this is an uncompressed file that was compressed by some mod_gzip-ish webserver thingy, so we will expand it # (expected_content should be uncompressed) @api.get("/nyan_cat_compressed.png", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { File.open(nyan_compressed_filename, "rb") do |f| f.read end } # # endpoints that set Content-Length correctly # # (expected_content should be uncompressed) @api.get("/nyan_cat_content_length.png", 200, nil, { 'Content-Length' => nyan_uncompressed_size.to_s, } ) { File.open(nyan_uncompressed_filename, "rb") do |f| f.read end } # (expected_content should be uncompressed) @api.get("/nyan_cat_content_length_compressed.png", 200, nil, { 'Content-Length' => nyan_compressed_size.to_s, 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { File.open(nyan_compressed_filename, "rb") do |f| f.read end } # # endpoints that simulate truncated downloads (bad content-length header) # # (expected_content should be uncompressed) @api.get("/nyan_cat_truncated.png", 200, nil, { 'Content-Length' => (nyan_uncompressed_size + 1).to_s, } ) { File.open(nyan_uncompressed_filename, "rb") do |f| f.read end } # (expected_content should be uncompressed) @api.get("/nyan_cat_truncated_compressed.png", 200, nil, { 'Content-Length' => (nyan_compressed_size + 1).to_s, 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { File.open(nyan_compressed_filename, "rb") do |f| f.read end } # # in the presence of a transfer-encoding header, we must ignore the content-length (this bad content-length should work) # # (expected_content should be uncompressed) @api.get("/nyan_cat_transfer_encoding.png", 200, nil, { 'Content-Length' => (nyan_uncompressed_size + 1).to_s, 'Transfer-Encoding' => 'anything', } ) { File.open(nyan_uncompressed_filename, "rb") do |f| f.read end } # # 403 with a Content-Length # @api.get('/forbidden', 403, 'Forbidden', { 'Content-Length' => 'Forbidden'.bytesize.to_s } ) @api.post('/posty', 200, 'Hi!') # # 400 with an error # @api.get('/bad_request', 400, '{ "error": [ "Your request is just terrible." ] }') @api.post('/bad_request', 400, '{ "error": [ "Your request is just terrible." ] }') end def stop_tiny_server @server.stop @server = @api = nil end end shared_examples_for "downloading all the things" do describe "when downloading a simple uncompressed file" do let(:source) { 'http://localhost:9000/nyan_cat.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "downloads requests correctly" end describe "when downloading a compressed file that should be left compressed" do let(:source) { 'http://localhost:9000/nyan_cat.png.gz' } let(:expected_content) { binread(nyan_compressed_filename) } # its the callers responsibility to disable_gzip when downloading a .gz url let(:http_client) { http_client_disable_gzip } it_behaves_like "downloads requests correctly" end describe "when downloading a file that has been compressed by the webserver" do let(:source) { 'http://localhost:9000/nyan_cat_compressed.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "downloads requests correctly" end describe "when downloading an uncompressed file with a correct content_length" do let(:source) { 'http://localhost:9000/nyan_cat_content_length.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "downloads requests correctly" end describe "when downloading a file that has been compressed by the webserver with a correct content_length" do let(:source) { 'http://localhost:9000/nyan_cat_content_length_compressed.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "downloads requests correctly" end describe "when downloading an uncompressed file that is truncated" do let(:source) { 'http://localhost:9000/nyan_cat_truncated.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "validates content length and throws an exception" end describe "when downloading a file that has been compressed by the webserver that is truncated" do let(:source) { 'http://localhost:9000/nyan_cat_truncated_compressed.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "validates content length and throws an exception" end describe "when downloading a file that has transfer encoding set with a bad content length that should be ignored" do let(:source) { 'http://localhost:9000/nyan_cat_transfer_encoding.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "downloads requests correctly" end describe "when downloading an endpoint that 403s" do let(:source) { 'http://localhost:9000/forbidden' } it_behaves_like "an endpoint that 403s" end describe "when downloading an endpoint that 403s" do let(:source) { 'http://localhost:9000/nyan_cat_content_length_compressed.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source2) { 'http://localhost:9000/forbidden' } it_behaves_like "a 403 after a successful request when reusing the request object" end end chef-12.3.0/spec/support/shared/functional/knife.rb0000644000004100000410000000214212520074675022240 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen () # Author:: Ho-Sheng Hsiao () # 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. # module SpecHelpers module Knife def redefine_argv(value) Object.send(:remove_const, :ARGV) Object.send(:const_set, :ARGV, value) end def with_argv(*argv) original_argv = ARGV redefine_argv(argv.flatten) begin yield ensure redefine_argv(original_argv) end end end end chef-12.3.0/spec/support/shared/functional/diff_disabled.rb0000644000004100000410000000023512520074675023704 0ustar www-datawww-data shared_context "diff disabled" do before do Chef::Config[:diff_disabled] = true end after do Chef::Config[:diff_disabled] = false end end chef-12.3.0/spec/support/shared/functional/directory_resource.rb0000644000004100000410000001215112520074675025060 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # shared_examples_for "a directory resource" do include_context "diff disabled" let(:expect_updated?) {true} context "when the target directory does not exist" do before do # assert pre-condition expect(File).not_to exist(path) end describe "when running action :create" do context "and the recursive option is not set" do before do resource.run_action(:create) end it "creates the directory when the :create action is run" do expect(File).to exist(path) end it "is marked updated by last action" do expect(resource).to be_updated_by_last_action end end context "and the recursive option is set" do before do expect(File).not_to exist(path) resource.recursive(true) @recursive_path = File.join(path, 'red-headed-stepchild') resource.path(@recursive_path) resource.run_action(:create) end it "recursively creates required directories" do expect(File).to exist(path) expect(File).to exist(@recursive_path) end it "is marked updated by last action" do expect(resource).to be_updated_by_last_action end end end # Set up the context for security tests def allowed_acl(sid, expected_perms) [ ACE.access_allowed(sid, expected_perms[:specific]), ACE.access_allowed(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)) ] end def denied_acl(sid, expected_perms) [ ACE.access_denied(sid, expected_perms[:specific]), ACE.access_denied(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)) ] end def parent_inheritable_acls dummy_directory_path = File.join(test_file_dir, "dummy_directory") dummy_directory = FileUtils.mkdir_p(dummy_directory_path) dummy_desc = get_security_descriptor(dummy_directory_path) FileUtils.rm_rf(dummy_directory_path) dummy_desc end it_behaves_like "a securable resource without existing target" end context "when the target directory exists" do before(:each) do # For resources such as remote_directory, simply creating the base # directory isn't enough to test that the system is in the desired state, # so we run the resource twice--otherwise the updated_by_last_action test # will fail. resource.dup.run_action(:create) expect(File).to exist(path) resource.run_action(:create) end describe "when running action :create" do before do resource.run_action(:create) end it "does not re-create the directory" do expect(File).to exist(path) end it "is not marked updated by last action" do expect(resource).not_to be_updated_by_last_action end end describe "when running action :delete" do context "without the recursive option" do before do resource.run_action(:delete) end it "deletes the directory" do expect(File).not_to exist(path) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end context "with the recursive option" do before do FileUtils.mkdir(File.join(path, 'red-headed-stepchild')) resource.recursive(true) resource.run_action(:delete) end it "recursively deletes directories" do expect(File).not_to exist(path) end end end end end shared_context Chef::Resource::Directory do # We create the directory than tmp to exercise different file # deployment strategies more completely. let(:test_file_dir) do if windows? File.join(ENV['systemdrive'], "test-dir") else File.join(CHEF_SPEC_DATA, "test-dir") end end let(:path) do File.join(test_file_dir, make_tmpname(directory_base)) end before do FileUtils::mkdir_p(test_file_dir) end after do FileUtils::rm_rf(test_file_dir) end after(:each) do FileUtils.rm_r(path) if File.exists?(path) end end chef-12.3.0/spec/support/platform_helpers.rb0000644000004100000410000000723112520074675021106 0ustar www-datawww-datarequire 'fcntl' require 'chef/mixin/shell_out' class ShellHelpers extend Chef::Mixin::ShellOut end def ruby_lt_20? !ruby_gte_20? end def chef_gte_13? Chef::VERSION.split('.').first.to_i >= 13 end def chef_lt_13? Chef::VERSION.split('.').first.to_i < 13 end def ruby_gte_19? RUBY_VERSION.to_f >= 1.9 end def ruby_20? !!(RUBY_VERSION =~ /^2.0/) end def windows? !!(RUBY_PLATFORM =~ /mswin|mingw|windows/) end def ohai # This is defined in spec_helper; it has the `platform` populated. OHAI_SYSTEM end require 'wmi-lite/wmi' if windows? def windows_domain_joined? return false unless windows? wmi = WmiLite::Wmi.new computer_system = wmi.first_of('Win32_ComputerSystem') computer_system['partofdomain'] end def windows_win2k3? return false unless windows? wmi = WmiLite::Wmi.new host = wmi.first_of('Win32_OperatingSystem') (host['version'] && host['version'].start_with?("5.2")) end def windows_2008r2_or_later? return false unless windows? wmi = WmiLite::Wmi.new host = wmi.first_of('Win32_OperatingSystem') version = host['version'] return false unless version components = version.split('.').map do | component | component.to_i end components.length >=2 && components[0] >= 6 && components[1] >= 1 end def windows_powershell_dsc? return false unless windows? supports_dsc = false begin wmi = WmiLite::Wmi.new('root/microsoft/windows/desiredstateconfiguration') lcm = wmi.query("SELECT * FROM meta_class WHERE __this ISA 'MSFT_DSCLocalConfigurationManager'") supports_dsc = !! lcm rescue WmiLite::WmiException end supports_dsc end def mac_osx_106? if File.exists? "/usr/bin/sw_vers" result = ShellHelpers.shell_out("/usr/bin/sw_vers") result.stdout.each_line do |line| if line =~ /^ProductVersion:\s10.6.*$/ return true end end end false end def mac_osx? if File.exists? "/usr/bin/sw_vers" result = ShellHelpers.shell_out("/usr/bin/sw_vers") result.stdout.each_line do |line| if line =~ /^ProductName:\sMac OS X.*$/ return true end end end false end # detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system) def windows64? windows? && ( ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' || ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' ) end # detects if the hardware is 32-bit def windows32? windows? && !windows64? end # def jruby? def unix? !windows? end def os_x? !!(RUBY_PLATFORM =~ /darwin/) end def solaris? !!(RUBY_PLATFORM =~ /solaris/) end def freebsd? !!(RUBY_PLATFORM =~ /freebsd/) end def aix? !!(RUBY_PLATFORM =~ /aix/) end def supports_cloexec? Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC') end DEV_NULL = windows? ? 'NUL' : '/dev/null' def selinux_enabled? # This code is currently copied from lib/chef/util/selinux to make # specs independent of product. selinuxenabled_path = which("selinuxenabled") if selinuxenabled_path cmd = Mixlib::ShellOut.new(selinuxenabled_path, :returns => [0,1]) cmd_result = cmd.run_command case cmd_result.exitstatus when 1 return false when 0 return true else raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}" end else # We assume selinux is not enabled if selinux utils are not # installed. return false end end def suse? File.exists?("/etc/SuSE-release") end def root? return false if windows? Process.euid == 0 end def openssl_gte_101? OpenSSL::OPENSSL_VERSION_NUMBER >= 10001000 end def openssl_lt_101? !openssl_gte_101? end def aes_256_gcm? OpenSSL::Cipher.ciphers.include?("aes-256-gcm") end chef-12.3.0/spec/functional/0000755000004100000410000000000012520074675015636 5ustar www-datawww-datachef-12.3.0/spec/functional/mixin/0000755000004100000410000000000012520074675016762 5ustar www-datawww-datachef-12.3.0/spec/functional/mixin/shell_out_spec.rb0000644000004100000410000000303412520074675022317 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Mixin::ShellOut do include Chef::Mixin::ShellOut describe "shell_out_with_systems_locale" do describe "when environment['LC_ALL'] is not set" do it "should use the default shell_out setting" do cmd = if windows? shell_out_with_systems_locale('echo %LC_ALL%') else shell_out_with_systems_locale('echo $LC_ALL') end expect(cmd.stdout.chomp).to match_environment_variable('LC_ALL') end end describe "when environment['LC_ALL'] is set" do it "should use the option's setting" do cmd = if windows? shell_out_with_systems_locale('echo %LC_ALL%', :environment => {'LC_ALL' => 'POSIX'}) else shell_out_with_systems_locale('echo $LC_ALL', :environment => {'LC_ALL' => 'POSIX'}) end expect(cmd.stdout.chomp).to eq 'POSIX' end end end end chef-12.3.0/spec/functional/rebooter_spec.rb0000644000004100000410000000627012520074675021023 0ustar www-datawww-data# # Author:: Chris Doherty ) # Copyright:: Copyright (c) 2014 Chef, 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::Platform::Rebooter do let(:reboot_info) do { :delay_mins => 5, :requested_by => "reboot resource functional test", :reason => "rebooter spec test" } end def create_resource resource = Chef::Resource::Reboot.new(expected[:requested_by], run_context) resource.delay_mins(expected[:delay_mins]) resource.reason(expected[:reason]) resource end let(:run_context) do node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, events) end let(:expected) do { :windows => 'shutdown /r /t 5 /c "rebooter spec test"', :linux => 'shutdown -r +5 "rebooter spec test"' } end let(:rebooter) { Chef::Platform::Rebooter } describe '#reboot_if_needed!' do it 'should not call #shell_out! when reboot has not been requested' do expect(rebooter).to receive(:shell_out!).exactly(0).times expect(rebooter).to receive(:reboot_if_needed!).once.and_call_original rebooter.reboot_if_needed!(run_context.node) end describe 'calling #shell_out! to reboot' do before(:each) do run_context.request_reboot(reboot_info) end after(:each) do run_context.cancel_reboot end shared_context 'test a reboot method' do def test_rebooter_method(method_sym, is_windows, expected_reboot_str) allow(Chef::Platform).to receive(:windows?).and_return(is_windows) expect(rebooter).to receive(:shell_out!).once.with(expected_reboot_str) expect(rebooter).to receive(method_sym).once.and_call_original rebooter.send(method_sym, run_context.node) end end describe 'when using #reboot_if_needed!' do include_context 'test a reboot method' it 'should produce the correct string on Windows' do test_rebooter_method(:reboot_if_needed!, true, expected[:windows]) end it 'should produce the correct (Linux-specific) string on non-Windows' do test_rebooter_method(:reboot_if_needed!, false, expected[:linux]) end end describe 'when using #reboot!' do include_context 'test a reboot method' it 'should produce the correct string on Windows' do test_rebooter_method(:reboot!, true, expected[:windows]) end it 'should produce the correct (Linux-specific) string on non-Windows' do test_rebooter_method(:reboot!, false, expected[:linux]) end end end end end chef-12.3.0/spec/functional/rest_spec.rb0000644000004100000410000000665512520074675020166 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'tiny_server' require 'support/shared/functional/http' describe Chef::REST do include ChefHTTPShared let(:http_client) { described_class.new(source) } let(:http_client_disable_gzip) { described_class.new(source, Chef::Config[:node_name], Chef::Config[:client_key], { :disable_gzip => true } ) } shared_examples_for "downloads requests correctly" do it "successfully downloads a streaming request" do tempfile = http_client.streaming_request(source, {}) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) end it "successfully downloads a GET request" do tempfile = http_client.get(source, {}) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) end end shared_examples_for "validates content length and throws an exception" do it "fails validation on a streaming download" do expect { http_client.streaming_request(source, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end it "fails validation on a GET request" do expect { http_client.get(source, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end shared_examples_for "an endpoint that 403s" do it "fails with a Net::HTTPServerException on a streaming download" do expect { http_client.streaming_request(source, {}) }.to raise_error(Net::HTTPServerException) end it "fails with a Net::HTTPServerException on a GET request" do expect { http_client.get(source, {}) }.to raise_error(Net::HTTPServerException) end end # see CHEF-5100 shared_examples_for "a 403 after a successful request when reusing the request object" do it "fails with a Net::HTTPServerException on a streaming download" do tempfile = http_client.streaming_request(source, {}) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) expect { http_client.streaming_request(source2, {}) }.to raise_error(Net::HTTPServerException) end it "fails with a Net::HTTPServerException on a GET request" do tempfile = http_client.get(source, {}) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) expect { http_client.get(source2, {}) }.to raise_error(Net::HTTPServerException) end end before do Chef::Config[:node_name] = "webmonkey.example.com" Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" end before(:all) do start_tiny_server end after(:all) do stop_tiny_server end it_behaves_like "downloading all the things" end chef-12.3.0/spec/functional/resource/0000755000004100000410000000000012520074675017465 5ustar www-datawww-datachef-12.3.0/spec/functional/resource/git_spec.rb0000644000004100000410000002177012520074675021616 0ustar www-datawww-data# # Author:: Seth Falcon () # 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/shell_out' require 'tmpdir' require 'shellwords' # Deploy relies heavily on symlinks, so it doesn't work on windows. describe Chef::Resource::Git do include Chef::Mixin::ShellOut let(:file_cache_path) { Dir.mktmpdir } # Some versions of git complains when the deploy directory is # already created. Here we intentionally don't create the deploy # directory beforehand. let(:base_dir_path) { Dir.mktmpdir } let(:deploy_directory) { File.join(base_dir_path, make_tmpname("git_base")) } let(:node) do Chef::Node.new.tap do |n| n.name "rspec-test" n.consume_external_attrs(@ohai.data, {}) end end let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) } # These tests use git's bundle feature, which is a way to export an entire # git repo (or subset of commits) as a single file. # # Generally you can treat a git bundle as a regular git remote. # # See also: http://git-scm.com/2010/03/10/bundles.html # # Beware that git bundles don't behave exactly the same as real # remotes. To get closer to real remotes, we'll create a local clone # of the bundle to use as a remote for the tests. This at least # gives the expected responses for ls-remote using git version # 1.7.12.4 let(:git_bundle_repo) { File.expand_path("git_bundles/example-repo.gitbundle", CHEF_SPEC_DATA) } let(:origin_repo_dir) { Dir.mktmpdir } let(:origin_repo) { "#{origin_repo_dir}/example" } # This is the fourth version let(:v1_commit) { "bc5ec79931ae74089aeadca6edc173527613e6d9" } let(:v1_tag) { "9b73fb5e316bfaff7b822b0ccb3e1e08f9885085" } let(:rev_foo) { "ed181b3419b6f489bedab282348162a110d6d3a1" } let(:rev_testing) { "972d153654503bccec29f630c5dd369854a561e8" } let(:rev_head) { "d294fbfd05aa7709ad9a9b8ef6343b17d355bf5f"} let(:git_user_config) do <<-E [user] name = frodoTbaggins email = frodo@shire.org E end before(:each) do Chef::Log.level = :warn # silence git command live streams @old_file_cache_path = Chef::Config[:file_cache_path] shell_out!("git clone \"#{git_bundle_repo}\" example", :cwd => origin_repo_dir) File.open("#{origin_repo}/.git/config", "a+") {|f| f.print(git_user_config) } Chef::Config[:file_cache_path] = file_cache_path end after(:each) do Chef::Config[:file_cache_path] = @old_file_cache_path FileUtils.remove_entry_secure deploy_directory if File.exist?(deploy_directory) FileUtils.remove_entry_secure file_cache_path end after(:all) do FileUtils.remove_entry_secure origin_repo_dir end before(:all) do @ohai = Ohai::System.new @ohai.all_plugins(["platform", "os"]) end context "working with pathes with special characters" do let(:path_with_spaces) { "#{origin_repo_dir}/path with spaces" } before(:each) do FileUtils.mkdir(path_with_spaces) FileUtils.cp(git_bundle_repo, path_with_spaces) end it "clones a repository with a space in the path" do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository "#{path_with_spaces}/example-repo.gitbundle" end.run_action(:sync) end end context "when deploying from an annotated tag" do let(:basic_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo r.revision "v1.0.0" end end # We create a copy of the basic_git_resource so that we can run # the resource again and verify that it doesn't update. let(:copy_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo r.revision "v1.0.0" end end it "checks out the revision pointed to by the tag commit, not the tag commit itself" do basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(v1_commit) # also verify the tag commit itself is what we expect as an extra sanity check rev = shell_out!('git rev-parse v1.0.0', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(rev).to eq(v1_tag) end it "doesn't update if up-to-date" do # this used to fail because we didn't resolve the annotated tag # properly to the pointed to commit. basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(v1_commit) copy_git_resource.run_action(:sync) expect(copy_git_resource).not_to be_updated end end context "when deploying from a SHA revision" do let(:basic_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository git_bundle_repo end end # We create a copy of the basic_git_resource so that we can run # the resource again and verify that it doesn't update. let(:copy_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo end end it "checks out the expected revision ed18" do basic_git_resource.revision rev_foo basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_foo) end it "doesn't update if up-to-date" do basic_git_resource.revision rev_foo basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_foo) copy_git_resource.revision rev_foo copy_git_resource.run_action(:sync) expect(copy_git_resource).not_to be_updated end it "checks out the expected revision 972d" do basic_git_resource.revision rev_testing basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_testing) end end context "when deploying from a revision named 'HEAD'" do let(:basic_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo r.revision 'HEAD' end end it "checks out the expected revision" do basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_head) end end context "when deploying from the default revision" do let(:basic_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo # use default end end it "checks out HEAD as the default revision" do basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_head) end end context "when dealing with a repo with a degenerate tag named 'HEAD'" do before do shell_out!("git tag -m\"degenerate tag\" HEAD ed181b3419b6f489bedab282348162a110d6d3a1", :cwd => origin_repo) end let(:basic_git_resource) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo r.revision 'HEAD' end end let(:git_resource_default_rev) do Chef::Resource::Git.new(deploy_directory, run_context).tap do |r| r.repository origin_repo # use default of revision end end it "checks out the (master) HEAD revision and ignores the tag" do basic_git_resource.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_head) end it "checks out the (master) HEAD revision when no revision is specified (ignores tag)" do git_resource_default_rev.run_action(:sync) head_rev = shell_out!('git rev-parse HEAD', :cwd => deploy_directory, :returns => [0]).stdout.strip expect(head_rev).to eq(rev_head) end end end chef-12.3.0/spec/functional/resource/batch_spec.rb0000644000004100000410000000173012520074675022106 0ustar www-datawww-data# # Author:: Adam Edwards () # 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::WindowsScript::Batch, :windows_only do include_context Chef::Resource::WindowsScript let(:output_command) { ' > ' } let (:architecture_command) { '@echo %PROCESSOR_ARCHITECTURE%' } it_behaves_like "a Windows script running on Windows" end chef-12.3.0/spec/functional/resource/dsc_script_spec.rb0000644000004100000410000003436412520074675023173 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/mixin/shell_out' require 'chef/mixin/windows_architecture_helper' describe Chef::Resource::DscScript, :windows_powershell_dsc_only do include Chef::Mixin::WindowsArchitectureHelper before(:all) do @temp_dir = ::Dir.mktmpdir("dsc-functional-test") end after(:all) do ::FileUtils.rm_rf(@temp_dir) if ::Dir.exist?(@temp_dir) end include Chef::Mixin::ShellOut def create_config_script_from_code(code, configuration_name, data = false) script_code = data ? code : "Configuration '#{configuration_name}'\n{\n\t#{code}\n}\n" data_suffix = data ? '_config_data' : '' extension = data ? 'psd1' : 'ps1' script_path = "#{@temp_dir}/dsc_functional_test#{data_suffix}.#{extension}" ::File.open(script_path, 'wt') do | script | script.write(script_code) end script_path end def user_exists?(target_user) result = false begin shell_out!("net user #{target_user}") result = true rescue Mixlib::ShellOut::ShellCommandFailed end result end def delete_user(target_user) begin shell_out!("net user #{target_user} /delete") rescue Mixlib::ShellOut::ShellCommandFailed end end let(:dsc_env_variable) { 'chefenvtest' } let(:dsc_env_value1) { 'value1' } let(:env_value2) { 'value2' } let(:dsc_test_run_context) { node = Chef::Node.new node.automatic['platform'] = 'windows' node.automatic['platform_version'] = '6.1' node.automatic['kernel'][:machine] = is_i386_process_on_x86_64_windows? ? :x86_64 : :i386 node.automatic[:languages][:powershell][:version] = '4.0' empty_events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, empty_events) } let(:dsc_test_resource_name) { 'DSCTest' } let(:dsc_test_resource_base) { Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context) } let(:test_registry_key) { 'HKEY_LOCAL_MACHINE\Software\Chef\Spec\Functional\Resource\dsc_script_spec' } let(:test_registry_value) { 'Registration' } let(:test_registry_data1) { 'LL927' } let(:test_registry_data2) { 'LL928' } let(:reg_key_name_param_name) { 'testregkeyname' } let(:reg_key_value_param_name) { 'testregvaluename' } let(:registry_embedded_parameters) { "$#{reg_key_name_param_name} = '#{test_registry_key}';$#{reg_key_value_param_name} = '#{test_registry_value}'"} let(:dsc_reg_code) { <<-EOH #{registry_embedded_parameters} Registry "ChefRegKey" { Key = $#{reg_key_name_param_name} ValueName = $#{reg_key_value_param_name} ValueData = '#{test_registry_data}' Ensure = 'Present' } EOH } let(:dsc_code) { dsc_reg_code } let(:dsc_reg_script) { <<-EOH param($testregkeyname, $testregvaluename) #{dsc_reg_code} EOH } let(:dsc_user_prefix) { 'dsc' } let(:dsc_user_suffix) { 'chefx' } let(:dsc_user) {"#{dsc_user_prefix}_usr_#{dsc_user_suffix}" } let(:dsc_user_prefix_env_var_name) { 'dsc_user_env_prefix' } let(:dsc_user_suffix_env_var_name) { 'dsc_user_env_suffix' } let(:dsc_user_prefix_env_code) { "$env:#{dsc_user_prefix_env_var_name}"} let(:dsc_user_suffix_env_code) { "$env:#{dsc_user_suffix_env_var_name}"} let(:dsc_user_prefix_param_name) { 'dsc_user_prefix_param' } let(:dsc_user_suffix_param_name) { 'dsc_user_suffix_param' } let(:dsc_user_prefix_param_code) { "$#{dsc_user_prefix_param_name}"} let(:dsc_user_suffix_param_code) { "$#{dsc_user_suffix_param_name}"} let(:dsc_user_env_code) { "\"$(#{dsc_user_prefix_env_code})_usr_$(#{dsc_user_suffix_env_code})\""} let(:dsc_user_param_code) { "\"$(#{dsc_user_prefix_param_code})_usr_$(#{dsc_user_suffix_param_code})\""} let(:config_flags) { nil } let(:config_params) { <<-EOH [CmdletBinding()] param ( $#{dsc_user_prefix_param_name}, $#{dsc_user_suffix_param_name} ) EOH } let(:config_param_section) { '' } let(:dsc_user_code) { "'#{dsc_user}'" } let(:dsc_user_prefix_code) { dsc_user_prefix } let(:dsc_user_suffix_code) { dsc_user_suffix } let(:dsc_script_environment_attribute) { nil } let(:dsc_user_resources_code) { <<-EOH #{config_param_section} node localhost { $testuser = #{dsc_user_code} $testpassword = ConvertTo-SecureString -String "jf9a8m49jrajf4#" -AsPlainText -Force $testcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testuser, $testpassword User dsctestusercreate { UserName = $testuser Password = $testcred Description = "DSC test user" Ensure = "Present" Disabled = $false PasswordNeverExpires = $true PasswordChangeRequired = $false } } EOH } let(:dsc_user_config_data) { <<-EOH @{ AllNodes = @( @{ NodeName = "localhost"; PSDscAllowPlainTextPassword = $true } ) } EOH } let(:dsc_environment_env_var_name) { 'dsc_test_cwd' } let(:dsc_environment_no_fail_not_etc_directory) { "#{ENV['systemroot']}\\system32" } let(:dsc_environment_fail_etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" } let(:exception_message_signature) { 'LL927-LL928' } let(:dsc_environment_config) {<<-EOH if (($pwd.path -eq '#{dsc_environment_fail_etc_directory}') -and (test-path('#{dsc_environment_fail_etc_directory}'))) { throw 'Signature #{exception_message_signature}: Purposefully failing because cwd == #{dsc_environment_fail_etc_directory}' } environment "whatsmydir" { Name = '#{dsc_environment_env_var_name}' Value = $pwd.path Ensure = 'Present' } EOH } let(:dsc_config_name) { dsc_test_resource_base.name } let(:dsc_resource_from_code) { dsc_test_resource_base.code(dsc_code) dsc_test_resource_base } let(:config_name_value) { dsc_test_resource_base.name } let(:dsc_resource_from_path) { dsc_test_resource_base.command(create_config_script_from_code(dsc_code, config_name_value)) dsc_test_resource_base } before(:each) do test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context) test_key_resource.recursive(true) test_key_resource.run_action(:delete_key) end after(:each) do test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context) test_key_resource.recursive(true) test_key_resource.run_action(:delete_key) end shared_examples_for 'a dsc_script resource with specified PowerShell configuration code' do let(:test_registry_data) { test_registry_data1 } it 'should create a registry key with a specific registry value and data' do expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false) dsc_test_resource.run_action(:run) expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true) expect(dsc_test_resource.registry_value_exists?(test_registry_key, {:name => test_registry_value, :type => :string, :data => test_registry_data})).to eq(true) end it_should_behave_like 'a dsc_script resource with configuration affected by cwd' end shared_examples_for 'a dsc_script resource with configuration affected by cwd' do after(:each) do removal_resource = Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context) removal_resource.code <<-EOH environment 'removethis' { Name = '#{dsc_environment_env_var_name}' Ensure = 'Absent' } EOH removal_resource.run_action(:run) end describe 'when the DSC configuration contains code that raises an exception if cwd has a specific value' do let(:dsc_code) { dsc_environment_config } it 'should not raise an exception if the cwd is not etc' do dsc_test_resource.cwd(dsc_environment_no_fail_not_etc_directory) expect {dsc_test_resource.run_action(:run)}.not_to raise_error end it 'should raise an exception if the cwd is etc' do dsc_test_resource.cwd(dsc_environment_fail_etc_directory) expect {dsc_test_resource.run_action(:run)}.to raise_error(Chef::Exceptions::PowershellCmdletException) begin dsc_test_resource.run_action(:run) rescue Chef::Exceptions::PowershellCmdletException => e expect(e.message).to match(exception_message_signature) end end end end shared_examples_for 'a parameterized DSC configuration script' do let(:dsc_user_prefix_code) { dsc_user_prefix_env_code } let(:dsc_user_suffix_code) { dsc_user_suffix_env_code } it_behaves_like 'a dsc_script with configuration that uses environment variables' end shared_examples_for 'a dsc_script without configuration data that takes parameters' do context 'when configuration data is not specified' do before(:each) do test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context) test_key_resource.recursive(true) test_key_resource.run_action(:delete_key) end after(:each) do test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context) test_key_resource.recursive(true) test_key_resource.run_action(:delete_key) end let(:test_registry_data) { test_registry_data1 } let(:dsc_parameterized_env_param_value) { "val" + Random::rand.to_s } it 'should have a default value of nil for the configuration_data attribute' do expect(dsc_test_resource.configuration_data).to eql(nil) end it 'should have a default value of nil for the configuration_data_path attribute' do expect(dsc_test_resource.configuration_data_script).to eql(nil) end let(:dsc_test_resource) { dsc_resource_from_path } let(:registry_embedded_parameters) { '' } let(:dsc_code) { dsc_reg_script } it 'should set a registry key according to parameters passed to the configuration' do dsc_test_resource.configuration_name(config_name_value) dsc_test_resource.flags({:"#{reg_key_name_param_name}" => test_registry_key, :"#{reg_key_value_param_name}" => test_registry_value}) expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(false) dsc_test_resource.run_action(:run) expect(dsc_test_resource.registry_key_exists?(test_registry_key)).to eq(true) expect(dsc_test_resource.registry_value_exists?(test_registry_key, {:name => test_registry_value, :type => :string, :data => test_registry_data})).to eq(true) end end end shared_examples_for 'a dsc_script with configuration data' do let(:configuration_data_attribute) { 'configuration_data' } it_behaves_like 'a dsc_script with configuration data set via an attribute' let(:configuration_data_attribute) { 'configuration_data_script' } it_behaves_like 'a dsc_script with configuration data set via an attribute' end shared_examples_for 'a dsc_script with configuration data set via an attribute' do it 'should run a configuration script that creates a user' do config_data_value = dsc_user_config_data dsc_test_resource.configuration_name(config_name_value) if configuration_data_attribute == 'configuration_data_script' config_data_value = create_config_script_from_code(dsc_user_config_data, '', true) end dsc_test_resource.environment({dsc_user_prefix_env_var_name => dsc_user_prefix, dsc_user_suffix_env_var_name => dsc_user_suffix}) dsc_test_resource.send(configuration_data_attribute, config_data_value) dsc_test_resource.flags(config_flags) expect(user_exists?(dsc_user)).to eq(false) expect {dsc_test_resource.run_action(:run)}.not_to raise_error expect(user_exists?(dsc_user)).to eq(true) end end shared_examples_for 'a dsc_script with configuration data that takes parameters' do let(:dsc_user_code) { dsc_user_param_code } let(:config_param_section) { config_params } let(:config_flags) {{:"#{dsc_user_prefix_param_name}" => "#{dsc_user_prefix}", :"#{dsc_user_suffix_param_name}" => "#{dsc_user_suffix}"}} it 'does not directly contain the user name' do configuration_script_content = ::File.open(dsc_test_resource.command) do | file | file.read end expect(configuration_script_content.include?(dsc_user)).to be(false) end it_behaves_like 'a dsc_script with configuration data' end shared_examples_for 'a dsc_script with configuration data that uses environment variables' do let(:dsc_user_code) { dsc_user_env_code } it 'does not directly contain the user name' do configuration_script_content = ::File.open(dsc_test_resource.command) do | file | file.read end expect(configuration_script_content.include?(dsc_user)).to be(false) end it_behaves_like 'a dsc_script with configuration data' end context 'when supplying configuration through the configuration attribute' do let(:dsc_test_resource) { dsc_resource_from_code } it_behaves_like 'a dsc_script resource with specified PowerShell configuration code' end context 'when supplying configuration using the path attribute' do let(:dsc_test_resource) { dsc_resource_from_path } it_behaves_like 'a dsc_script resource with specified PowerShell configuration code' end context 'when running a configuration that manages users' do before(:each) do delete_user(dsc_user) end let(:dsc_code) { dsc_user_resources_code } let(:config_name_value) { 'DSCTestConfig' } let(:dsc_test_resource) { dsc_resource_from_path } it_behaves_like 'a dsc_script with configuration data' it_behaves_like 'a dsc_script with configuration data that uses environment variables' it_behaves_like 'a dsc_script with configuration data that takes parameters' it_behaves_like 'a dsc_script without configuration data that takes parameters' end end chef-12.3.0/spec/functional/resource/deploy_revision_spec.rb0000644000004100000410000007400712520074675024246 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'tmpdir' # Deploy relies heavily on symlinks, so it doesn't work on windows. describe Chef::Resource::DeployRevision, :unix_only => true do let(:file_cache_path) { Dir.mktmpdir } let(:deploy_directory) { Dir.mktmpdir } # By making restart or other operations write to this file, we can externally # track the order in which those operations happened. let(:observe_order_file) { Tempfile.new("deploy-resource-observe-operations") } before do Chef::Log.level = :info @old_file_cache_path = Chef::Config[:file_cache_path] Chef::Config[:file_cache_path] = file_cache_path end after do Chef::Config[:file_cache_path] = @old_file_cache_path FileUtils.remove_entry_secure deploy_directory if File.exist?(deploy_directory) FileUtils.remove_entry_secure file_cache_path observe_order_file.close FileUtils.remove_entry_secure observe_order_file.path end before(:all) do @ohai = Ohai::System.new @ohai.all_plugins(["platform", "os"]) end let(:node) do Chef::Node.new.tap do |n| n.name "rspec-test" n.consume_external_attrs(@ohai.data, {}) end end let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new } let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) } # These tests use git's bundle feature, which is a way to export an entire # git repo (or subset of commits) as a single file. # # Generally you can treat a git bundle as a regular git remote. # # See also: http://git-scm.com/2010/03/10/bundles.html let(:git_bundle_repo) { File.expand_path("git_bundles/sinatra-test-app.gitbundle", CHEF_SPEC_DATA) } let(:git_bundle_with_in_repo_callbacks) { File.expand_path("git_bundles/sinatra-test-app-with-callback-files.gitbundle", CHEF_SPEC_DATA) } let(:git_bundle_with_in_repo_symlinks) { File.expand_path("git_bundles/sinatra-test-app-with-symlinks.gitbundle", CHEF_SPEC_DATA) } # This is the fourth version let(:latest_rev) { "3eb5ca6c353c83d9179dd3b29347539829b401f3" } # This is the third version let(:previous_rev) { "6d19a6dbecc8e37f5b2277345885c0c783eb8fb1" } # This is the second version let(:second_rev) { "0827e1b0e5043608ac0a824da5c558e252154ad0" } # This is the sixth version, it is on the "with-deploy-scripts" branch let(:rev_with_in_repo_callbacks) { "2404d015882659754bdb93ad6e4b4d3d02691a82" } # This is the fifth version in the "with-symlinks" branch let(:rev_with_in_repo_symlinks) { "5a4748c52aaea8250b4346a9b8ede95ee3755e28" } # Read values from the +observe_order_file+ and split each line. This way you # can see in which order things really happened. def actual_operations_order IO.read(observe_order_file.path).split("\n").map(&:strip) end # 1. touch `restart.txt` in cwd so we know that the command is run with the # right cwd. # 2. Append +tag+ to the `observe_order_file` so we can check the order in # which operations happen later in the test. def shell_restart_command(tag) "touch restart.txt && echo '#{tag}' >> #{observe_order_file.path}" end let(:basic_deploy_resource) do Chef::Resource::DeployRevision.new(deploy_directory, run_context).tap do |r| r.name "deploy-revision-unit-test" r.repo git_bundle_repo r.symlink_before_migrate({}) r.symlinks({}) end end let(:deploy_to_latest_rev) do basic_deploy_resource.dup.tap do |r| r.revision(latest_rev) r.restart_command shell_restart_command(:deploy_to_latest_rev) end end let(:deploy_to_previous_rev) do basic_deploy_resource.dup.tap do |r| r.revision(previous_rev) r.restart_command shell_restart_command(:deploy_to_previous_rev) end end let(:deploy_to_latest_rev_again) do basic_deploy_resource.dup.tap do |r| r.revision(latest_rev) r.restart_command shell_restart_command(:deploy_to_latest_rev_again) end end let(:deploy_to_previous_rev_again) do basic_deploy_resource.dup.tap do |r| r.revision(previous_rev) r.restart_command shell_restart_command(:deploy_to_previous_rev_again) end end let(:deploy_to_second_rev) do basic_deploy_resource.dup.tap do |r| r.revision(second_rev) r.restart_command shell_restart_command(:deploy_to_second_rev) end end let(:deploy_to_second_rev_again) do basic_deploy_resource.dup.tap do |r| r.revision(second_rev) r.restart_command shell_restart_command(:deploy_to_second_rev_again) end end let(:deploy_to_second_rev_again_again) do basic_deploy_resource.dup.tap do |r| r.revision(second_rev) r.restart_command shell_restart_command(:deploy_to_second_rev_again_again) end end # Computes the full path for +path+ relative to the deploy directory def rel_path(path) File.expand_path(path, deploy_directory) end def actual_current_rev Dir.chdir(rel_path("current")) do `git rev-parse HEAD`.strip end end def self.the_app_is_deployed_at_revision(target_rev_spec) it "deploys the app to the target revision (#{target_rev_spec})" do target_rev = send(target_rev_spec) expect(File).to exist(rel_path("current")) expect(actual_current_rev).to eq(target_rev) # Is the app code actually there? expect(File).to exist(rel_path("current/app/app.rb")) end end context "when deploying a simple app" do describe "for the first time, with the required directory layout precreated" do before do FileUtils.mkdir_p(rel_path("releases")) FileUtils.mkdir_p(rel_path("shared")) deploy_to_latest_rev.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the application" do expect(File).to exist(rel_path("current/restart.txt")) expect(actual_operations_order).to eq(%w[deploy_to_latest_rev]) end it "is marked as updated" do expect(deploy_to_latest_rev).to be_updated_by_last_action end end describe "back to a previously deployed revision, with the directory structure precreated" do before do FileUtils.mkdir_p(rel_path("releases")) FileUtils.mkdir_p(rel_path("shared")) deploy_to_latest_rev.run_action(:deploy) deploy_to_previous_rev.run_action(:deploy) deploy_to_latest_rev_again.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again]) end it "is marked updated" do expect(deploy_to_latest_rev_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app") end end describe "for the first time, with no existing directory layout" do before do deploy_to_latest_rev.run_action(:deploy) end it "creates the required directory tree" do expect(File).to be_directory(rel_path("releases")) expect(File).to be_directory(rel_path("shared")) expect(File).to be_directory(rel_path("releases/#{latest_rev}")) expect(File).to be_directory(rel_path("current/tmp")) expect(File).to be_directory(rel_path("current/config")) expect(File).to be_directory(rel_path("current/public")) expect(File).to be_symlink(rel_path("current")) expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}")) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the application" do expect(File).to exist(rel_path("current/restart.txt")) expect(actual_operations_order).to eq(%w[deploy_to_latest_rev]) end it "is marked as updated" do expect(deploy_to_latest_rev).to be_updated_by_last_action end end describe "again to the current revision" do before do deploy_to_latest_rev.run_action(:deploy) deploy_to_latest_rev.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "does not restart the app" do expect(actual_operations_order).to eq(%w[deploy_to_latest_rev]) end it "is not marked updated" do expect(deploy_to_latest_rev).not_to be_updated_by_last_action end end describe "again with force_deploy" do before do deploy_to_latest_rev.run_action(:force_deploy) deploy_to_latest_rev_again.run_action(:force_deploy) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the app" do expect(actual_operations_order).to eq(%w[deploy_to_latest_rev deploy_to_latest_rev_again]) end it "is marked updated" do expect(deploy_to_latest_rev).to be_updated_by_last_action end end describe "again to a new revision" do before do deploy_to_previous_rev.run_action(:deploy) deploy_to_latest_rev.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the application after the new deploy" do expect(actual_operations_order).to eq(%w[deploy_to_previous_rev deploy_to_latest_rev]) end it "is marked updated" do expect(deploy_to_previous_rev).to be_updated_by_last_action end it "leaves the old copy of the app around for rollback" do expect(File).to exist(File.join(deploy_directory, "releases", previous_rev)) end end describe "back to a previously deployed revision (implicit rollback)" do before do deploy_to_latest_rev.run_action(:deploy) deploy_to_previous_rev.run_action(:deploy) deploy_to_latest_rev_again.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_latest_rev deploy_to_previous_rev deploy_to_latest_rev_again]) end it "is marked updated" do expect(deploy_to_latest_rev_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the fourth version of the app") end end describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do before do deploy_to_previous_rev.run_action(:deploy) @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases deploy_to_latest_rev.run_action(:deploy) @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases deploy_to_latest_rev_again.run_action(:rollback) @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases end the_app_is_deployed_at_revision(:previous_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_previous_rev deploy_to_latest_rev deploy_to_latest_rev_again]) end it "is marked updated" do expect(deploy_to_latest_rev_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app") end it "all_releases after first deploy should have one entry" do expect(@previous_rev_all_releases.length).to eq(1) end it "all_releases after second deploy should have two entries" do expect(@latest_rev_all_releases.length).to eq(2) end it "all_releases after rollback should have one entry" do expect(@previous_rev_again_all_releases.length).to eq(1) end it "all_releases after rollback should be the same as after the first deploy" do expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases) end end describe "back to a previously deployed revision where resource rev == previous revision (explicit rollback)" do before do deploy_to_previous_rev.run_action(:deploy) @previous_rev_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases deploy_to_latest_rev.run_action(:deploy) @latest_rev_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases deploy_to_previous_rev_again.run_action(:rollback) # FIXME: only difference with previous test is using latest_rev_again insetad of previous_rev_again @previous_rev_again_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases end the_app_is_deployed_at_revision(:previous_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_previous_rev deploy_to_latest_rev deploy_to_previous_rev_again]) end it "is marked updated" do expect(deploy_to_previous_rev_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the third version of the app") end it "all_releases after first deploy should have one entry" do expect(@previous_rev_all_releases.length).to eq(1) end it "all_releases after second deploy should have two entries" do expect(@latest_rev_all_releases.length).to eq(2) end it "all_releases after rollback should have one entry" do expect(@previous_rev_again_all_releases.length).to eq(1) end it "all_releases after rollback should be the same as after the first deploy" do expect(@previous_rev_again_all_releases).to eq(@previous_rev_all_releases) end end describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do before do deploy_to_second_rev.run_action(:deploy) @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases deploy_to_previous_rev.run_action(:deploy) @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases deploy_to_previous_rev_again.run_action(:rollback) @third_deploy_all_releases = deploy_to_previous_rev_again.provider_for_action(:deploy).all_releases deploy_to_latest_rev.run_action(:deploy) @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases deploy_to_latest_rev_again.run_action(:rollback) @fifth_deploy_all_releases = deploy_to_latest_rev_again.provider_for_action(:deploy).all_releases end the_app_is_deployed_at_revision(:second_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_second_rev deploy_to_previous_rev deploy_to_previous_rev_again deploy_to_latest_rev deploy_to_latest_rev_again]) end it "is marked updated" do expect(deploy_to_latest_rev_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app") end it "all_releases after rollback should have one entry" do expect(@fifth_deploy_all_releases.length).to eq(1) end it "all_releases after rollback should be the same as after the first deploy" do expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases) end end describe "back to a previously deployed revision where resource rev == latest revision (explicit rollback)" do before do deploy_to_second_rev.run_action(:deploy) @first_deploy_all_releases = deploy_to_second_rev.provider_for_action(:deploy).all_releases deploy_to_previous_rev.run_action(:deploy) @second_deploy_all_releases = deploy_to_previous_rev.provider_for_action(:deploy).all_releases deploy_to_second_rev_again.run_action(:rollback) @third_deploy_all_releases = deploy_to_second_rev_again.provider_for_action(:deploy).all_releases deploy_to_latest_rev.run_action(:deploy) @fourth_deploy_all_releases = deploy_to_latest_rev.provider_for_action(:deploy).all_releases deploy_to_second_rev_again_again.run_action(:rollback) @fifth_deploy_all_releases = deploy_to_second_rev_again_again.provider_for_action(:deploy).all_releases end the_app_is_deployed_at_revision(:second_rev) it "restarts the application after rolling back" do expect(actual_operations_order).to eq(%w[deploy_to_second_rev deploy_to_previous_rev deploy_to_second_rev_again deploy_to_latest_rev deploy_to_second_rev_again_again]) end it "is marked updated" do expect(deploy_to_second_rev_again_again).to be_updated_by_last_action end it "deploys the right code" do expect(IO.read(rel_path("current/app/app.rb"))).to include("this is the second version of the app") end it "all_releases after rollback should have one entry" do expect(@fifth_deploy_all_releases.length).to eq(1) end it "all_releases after rollback should be the same as after the first deploy" do expect(@fifth_deploy_all_releases).to eq(@first_deploy_all_releases) end end # CHEF-3435 describe "to a deploy_to path that does not yet exist" do let(:top_level_tmpdir) { Dir.mktmpdir } # override top level deploy_directory let block with one that is two # directories deeper let(:deploy_directory) { File.expand_path("nested/deeper", top_level_tmpdir) } after do FileUtils.remove_entry_secure top_level_tmpdir end before do expect(File).not_to exist(deploy_directory) deploy_to_latest_rev.run_action(:deploy) end it "creates the required directory tree" do expect(File).to be_directory(rel_path("releases")) expect(File).to be_directory(rel_path("shared")) expect(File).to be_directory(rel_path("releases/#{latest_rev}")) expect(File).to be_directory(rel_path("current/tmp")) expect(File).to be_directory(rel_path("current/config")) expect(File).to be_directory(rel_path("current/public")) expect(File).to be_symlink(rel_path("current")) expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}")) end the_app_is_deployed_at_revision(:latest_rev) end end context "when deploying an app with inline recipe callbacks" do # Use closures to capture and mutate this variable. This allows us to track # ordering of operations. callback_order = [] let(:deploy_to_latest_with_inline_recipes) do deploy_to_latest_rev.dup.tap do |r| r.symlink_before_migrate "config/config.ru" => "config.ru" r.before_migrate do callback_order << :before_migrate file "#{release_path}/before_migrate.txt" do # The content here isn't relevant, but it gets printed when running # the tests. Could be handy for debugging. content callback_order.inspect end end r.before_symlink do callback_order << :before_symlink current_release_path = release_path ruby_block "ensure before symlink" do block do if ::File.exist?(::File.join(current_release_path, "/tmp")) raise "Ordering issue with provider, expected symlinks to not have been created" end end end file "#{release_path}/before_symlink.txt" do content callback_order.inspect end end r.before_restart do callback_order << :before_restart current_release_path = release_path ruby_block "ensure after symlink" do block do unless ::File.exist?(::File.join(current_release_path, "/tmp")) raise "Ordering issue with provider, expected symlinks to have been created" end end end file "#{release_path}/tmp/before_restart.txt" do content callback_order.inspect end end r.after_restart do callback_order << :after_restart file "#{release_path}/tmp/after_restart.txt" do content callback_order.inspect end end end end before do callback_order.clear # callback_order variable is global for this context group deploy_to_latest_with_inline_recipes.run_action(:deploy) end the_app_is_deployed_at_revision(:latest_rev) it "is marked updated" do expect(deploy_to_latest_with_inline_recipes).to be_updated_by_last_action end it "calls the callbacks in order" do expect(callback_order).to eq([:before_migrate, :before_symlink, :before_restart, :after_restart]) end it "runs chef resources in the callbacks" do expect(File).to exist(rel_path("current/before_migrate.txt")) expect(File).to exist(rel_path("current/before_symlink.txt")) expect(File).to exist(rel_path("current/tmp/before_restart.txt")) expect(File).to exist(rel_path("current/tmp/after_restart.txt")) end end context "when deploying an app with in-repo callback scripts" do let(:deploy_with_in_repo_callbacks) do basic_deploy_resource.dup.tap do |r| r.repo git_bundle_with_in_repo_callbacks r.revision rev_with_in_repo_callbacks end end before do deploy_with_in_repo_callbacks.run_action(:deploy) end the_app_is_deployed_at_revision(:rev_with_in_repo_callbacks) it "runs chef resources in the callbacks" do expect(File).to exist(rel_path("current/before_migrate.txt")) expect(File).to exist(rel_path("current/before_symlink.txt")) expect(File).to exist(rel_path("current/tmp/before_restart.txt")) expect(File).to exist(rel_path("current/tmp/after_restart.txt")) end end context "when deploying an app with migrations" do let(:deploy_with_migration) do basic_deploy_resource.dup.tap do |r| # Need this so we can call methods from this test inside the inline # recipe callbacks spec_context = self r.revision latest_rev # enable migrations r.migrate true # abuse `shell_restart_command` so we can observe order of when the # miration command gets run r.migration_command shell_restart_command("migration") r.before_migrate do # inline recipe callbacks don't cwd, so you have to get the release # directory as a local and "capture" it in the closure. current_release = release_path execute spec_context.shell_restart_command("before_migrate") do cwd current_release end end r.before_symlink do current_release = release_path execute spec_context.shell_restart_command("before_symlink") do cwd current_release end end r.before_restart do current_release = release_path execute spec_context.shell_restart_command("before_restart") do cwd current_release end end r.after_restart do current_release = release_path execute spec_context.shell_restart_command("after_restart") do cwd current_release end end end end before do deploy_with_migration.run_action(:deploy) end it "runs migrations in between the before_migrate and before_symlink steps" do expect(actual_operations_order).to eq(%w[before_migrate migration before_symlink before_restart after_restart]) end end context "when deploying an app with in-repo symlinks" do let(:deploy_with_in_repo_symlinks) do basic_deploy_resource.dup.tap do |r| r.repo git_bundle_with_in_repo_symlinks r.revision rev_with_in_repo_symlinks end end it "should not raise an exception calling File.utime on symlinks" do expect { deploy_with_in_repo_symlinks.run_action(:deploy) }.not_to raise_error end end context "when a previously deployed application has been nuked" do shared_examples_for "a redeployed application" do it "should redeploy the application" do expect(File).to be_directory(rel_path("releases")) expect(File).to be_directory(rel_path("shared")) expect(File).to be_directory(rel_path("releases/#{latest_rev}")) expect(File).to be_directory(rel_path("current/tmp")) expect(File).to be_directory(rel_path("current/config")) expect(File).to be_directory(rel_path("current/public")) expect(File).to be_symlink(rel_path("current")) expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}")) end end # background: If a deployment is hosed and the user decides to rm -rf the # deployment dir, deploy resource should detect that and nullify its cache. context "by removing the entire deploy directory" do before do deploy_to_latest_rev.dup.run_action(:deploy) FileUtils.rm_rf(deploy_directory) deploy_to_latest_rev.dup.run_action(:deploy) end include_examples "a redeployed application" end context "by removing the current/ directory" do before do deploy_to_latest_rev.dup.run_action(:deploy) FileUtils.rm(rel_path("current")) deploy_to_latest_rev.dup.run_action(:deploy) end include_examples "a redeployed application" end end context "when a deployment fails" do shared_examples_for "a recovered deployment" do it "should redeploy the application" do expect(File).to be_directory(rel_path("releases")) expect(File).to be_directory(rel_path("shared")) expect(File).to be_directory(rel_path("releases/#{latest_rev}")) expect(File).to be_directory(rel_path("current/tmp")) expect(File).to be_directory(rel_path("current/config")) expect(File).to be_directory(rel_path("current/public")) expect(File).to be_symlink(rel_path("current")) expect(File.readlink(rel_path("current"))).to eq(rel_path("releases/#{latest_rev}")) # if callbacks ran, we know the app was deployed and not merely rolled # back to a (busted) prior deployment. expect(callback_order).to eq([:before_migrate, :before_symlink, :before_restart, :after_restart ]) end end let!(:callback_order) { [] } let(:deploy_to_latest_with_callback_tracking) do resource = deploy_to_latest_rev.dup tracker = callback_order resource.before_migrate { tracker << :before_migrate } resource.before_symlink { tracker << :before_symlink } resource.before_restart { tracker << :before_restart } resource.after_restart { tracker << :after_restart } resource end [:before_migrate, :before_symlink, :before_restart, :after_restart].each do |callback| context "in the `#{callback}' callback" do before do expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy}) deploy_to_latest_with_callback_tracking.run_action(:deploy) end let(:deploy_that_fails) do resource = deploy_to_latest_rev.dup errant_callback = lambda {|x| raise Exception, "I am a failed deploy" } resource.send(callback, &errant_callback) resource end include_examples "a recovered deployment" end end context "in the service restart step" do let(:deploy_that_fails) do resource = deploy_to_latest_rev.dup resource.restart_command("RUBYOPT=\"\" ruby -e 'exit 1'") resource end before do expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Chef::Exceptions::Exec) deploy_to_latest_with_callback_tracking.run_action(:deploy) end include_examples "a recovered deployment" end context "when cloning the app code" do class BadTimeScmProvider def initialize(new_resource, run_context) end def load_current_resource end def revision_slug "5" end def run_action(action) raise RuntimeError, "network error" end end let(:deploy_that_fails) do resource = deploy_to_latest_rev.dup resource.scm_provider(BadTimeScmProvider) resource end before do expect { deploy_that_fails.run_action(:deploy) }.to raise_error(RuntimeError, /network error/) deploy_to_latest_with_callback_tracking.run_action(:deploy) end include_examples "a recovered deployment" end context "and then is deployed to a different revision" do let(:deploy_that_fails) do resource = deploy_to_previous_rev.dup resource.after_restart {|x| raise Exception, "I am a failed deploy" } resource end before do expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Exception, %r{I am a failed deploy}) deploy_to_latest_rev.run_action(:deploy) end it "removes the unsuccessful deploy after a later successful deploy" do expect(::File).not_to exist(File.join(deploy_directory, "releases", previous_rev)) end end end end chef-12.3.0/spec/functional/resource/group_spec.rb0000644000004100000410000003400612520074675022163 0ustar www-datawww-data# # Author:: Chirag Jog () # Author:: Siddheshwar More () # 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/base' require 'chef/mixin/shell_out' # Chef::Resource::Group are turned off on Mac OS X 10.6 due to caching # issues around Etc.getgrnam() not picking up the group membership # changes that are done on the system. Etc.endgrent is not functioning # correctly on certain 10.6 boxes. describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supported_on_mac_osx_106 do include Chef::Mixin::ShellOut def group_should_exist(group) case ohai[:platform_family] when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch" expect { Etc::getgrnam(group) }.not_to raise_error expect(group).to eq(Etc::getgrnam(group).name) when "windows" expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.not_to raise_error end end def user_exist_in_group?(user) case ohai[:platform_family] when "windows" user_sid = sid_string_from_user(user) user_sid.nil? ? false : Chef::Util::Windows::NetGroup.new(group_name).local_get_members.include?(user_sid) when "mac_os_x" membership_info = shell_out("dscl . -read /Groups/#{group_name}").stdout members = membership_info.split(" ") members.shift # Get rid of GroupMembership: string members.include?(user) else Etc::getgrnam(group_name).mem.include?(user) end end def group_should_not_exist(group) case ohai[:platform_family] when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch" expect { Etc::getgrnam(group) }.to raise_error(ArgumentError, "can't find group for #{group}") when "windows" expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.to raise_error(ArgumentError, "The group name could not be found.") end end def compare_gid(resource, gid) return resource.gid == Etc::getgrnam(resource.name).gid if unix? end def sid_string_from_user(user) begin sid = Chef::ReservedNames::Win32::Security.lookup_account_name(user) rescue Chef::Exceptions::Win32APIError sid = nil end sid.nil? ? nil : sid[1].to_s end def windows_domain_user?(user_name) domain, user = user_name.split('\\') if user && domain != '.' computer_name = ENV['computername'] domain.downcase != computer_name.downcase end end def user(username) usr = Chef::Resource::User.new("#{username}", run_context) if ohai[:platform_family] == "windows" usr.password("ComplexPass11!") end usr end def create_user(username) user(username).run_action(:create) if ! windows_domain_user?(username) # TODO: User shouldn't exist end def remove_user(username) user(username).run_action(:remove) if ! windows_domain_user?(username) # TODO: User shouldn't exist end shared_examples_for "correct group management" do def add_members_to_group(members) temp_resource = group_resource.dup temp_resource.members(members) temp_resource.excluded_members([ ]) temp_resource.append(true) temp_resource.run_action(:modify) members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end end def create_group temp_resource = group_resource.dup temp_resource.members([ ]) temp_resource.excluded_members([ ]) temp_resource.run_action(:create) group_should_exist(group_name) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end before(:each) do create_group end after(:each) do group_resource.run_action(:remove) group_should_not_exist(group_name) end describe "when append is not set" do let(:included_members) { [spec_members[1]] } before do create_user(spec_members[1]) create_user(spec_members[0]) add_members_to_group([spec_members[0]]) end after do remove_user(spec_members[1]) remove_user(spec_members[0]) end it "should remove the existing users and add the new users to the group" do group_resource.run_action(tested_action) expect(user_exist_in_group?(spec_members[1])).to eq(true) expect(user_exist_in_group?(spec_members[0])).to eq(false) end end describe "when append is set" do before(:each) do group_resource.append(true) end describe "when the users exist" do before do (included_members + excluded_members).each do |member| create_user(member) end end after do (included_members + excluded_members).each do |member| remove_user(member) end end it "should add included members to the group" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end excluded_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end describe "when group contains some users" do before(:each) do add_members_to_group([ spec_members[0], spec_members[2] ]) end it "should add the included users and remove excluded users" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end excluded_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end end end describe "when the users doesn't exist" do describe "when append is not set" do it "should raise an error" do expect { @grp_resource.run_action(tested_action) }.to raise_error end end describe "when append is set" do it "should raise an error" do expect { @grp_resource.run_action(tested_action) }.to raise_error end end end end end shared_examples_for "an expected invalid domain error case" do let(:invalid_domain_user_name) { "no space\\administrator" } let(:nonexistent_domain_user_name) { "xxfakedom\\administrator" } before(:each) do group_resource.members [] group_resource.excluded_members [] group_resource.append(true) group_resource.run_action(:create) group_should_exist(group_name) end describe "when updating membership" do it "raises an error for a non well-formed domain name" do group_resource.members [invalid_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end it "raises an error for a nonexistent domain" do group_resource.members [nonexistent_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end end describe "when removing members" do it "raises an error for a non well-formed domain name" do group_resource.excluded_members [invalid_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end it "raises an error for a nonexistent domain" do group_resource.excluded_members [nonexistent_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end end end let(:group_name) { "t-#{SecureRandom.random_number(9999)}" } let(:included_members) { nil } let(:excluded_members) { nil } let(:group_resource) { group = Chef::Resource::Group.new(group_name, run_context) group.members(included_members) group.excluded_members(excluded_members) group } it "append should be false by default" do expect(group_resource.append).to eq(false) end describe "group create action" do after(:each) do group_resource.run_action(:remove) group_should_not_exist(group_name) end it "should create a group" do group_resource.run_action(:create) group_should_exist(group_name) end describe "when group name is length 256", :windows_only do let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\ smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" } it "should create a group" do group_resource.run_action(:create) group_should_exist(group_name) end end describe "when group name length is more than 256", :windows_only do let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\ smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" } it "should not create a group" do expect { group_resource.run_action(:create) }.to raise_error group_should_not_exist(group_name) end end # not_supported_on_solaris because of the use of excluded_members describe "should raise an error when same member is included in the members and excluded_members", :not_supported_on_solaris do it "should raise an error" do invalid_resource = group_resource.dup invalid_resource.members(["Jack"]) invalid_resource.excluded_members(["Jack"]) expect { invalid_resource.run_action(:create)}.to raise_error(Chef::Exceptions::ConflictingMembersInGroup) end end end describe "group remove action" do describe "when there is a group" do before do group_resource.run_action(:create) group_should_exist(group_name) end it "should remove a group" do group_resource.run_action(:remove) group_should_not_exist(group_name) end end describe "when there is no group" do it "should be no-op" do group_resource.run_action(:remove) group_should_not_exist(group_name) end end end describe "group modify action", :not_supported_on_solaris do let(:spec_members){ ["Gordon", "Eric", "Anthony"] } let(:included_members) { [spec_members[0], spec_members[1]] } let(:excluded_members) { [spec_members[2]] } let(:tested_action) { :modify } describe "when there is no group" do it "should raise an error" do expect { group_resource.run_action(:modify) }.to raise_error end end describe "when there is a group" do it_behaves_like "correct group management" end describe "when running on Windows", :windows_only do describe "when members are Active Directory domain identities", :windows_domain_joined_only do let(:computer_domain) { ohai[:kernel]['cs_info']['domain'].split('.')[0] } let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] } include_examples "correct group management" end it_behaves_like "an expected invalid domain error case" end end describe "group manage action", :not_supported_on_solaris do let(:spec_members){ ["Gordon", "Eric", "Anthony"] } let(:included_members) { [spec_members[0], spec_members[1]] } let(:excluded_members) { [spec_members[2]] } let(:tested_action) { :manage } describe "when there is no group" do it "raises an error on modify" do expect { group_resource.run_action(:modify) }.to raise_error end it "does not raise an error on manage" do expect { group_resource.run_action(:manage) }.not_to raise_error end end describe "when there is a group" do it_behaves_like "correct group management" end describe "running on windows", :windows_only do describe "when members are Windows domain identities", :windows_domain_joined_only do let(:computer_domain) { ohai[:kernel]['cs_info']['domain'].split('.')[0] } let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] } include_examples "correct group management" end it_behaves_like "an expected invalid domain error case" end end describe "group resource with Usermod provider", :solaris_only do describe "when excluded_members is set" do let(:excluded_members) { ["Anthony"] } it ":manage should raise an error" do expect {group_resource.run_action(:manage) }.to raise_error end it ":modify should raise an error" do expect {group_resource.run_action(:modify) }.to raise_error end it ":create should raise an error" do expect {group_resource.run_action(:create) }.to raise_error end end describe "when append is not set" do let(:included_members) { ["Gordon", "Eric"] } before(:each) do group_resource.append(false) end it ":manage should raise an error" do expect {group_resource.run_action(:manage) }.to raise_error end it ":modify should raise an error" do expect {group_resource.run_action(:modify) }.to raise_error end end end end chef-12.3.0/spec/functional/resource/bash_spec.rb0000644000004100000410000000614112520074675021743 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 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/base' describe Chef::Resource::Bash, :unix_only do let(:code) { "echo hello" } let(:resource) { resource = Chef::Resource::Bash.new("foo_resource", run_context) resource.code(code) resource } describe "when setting the command attribute" do let (:command) { 'wizard racket' } # in Chef-12 the `command` attribute is largely useless, but does set the identity attribute # so that notifications need to target the value of the command. it will not run the `command` # and if it is given without a code block then it does nothing and always succeeds. describe "in Chef-12", :chef_lt_13_only do it "gets the commmand attribute from the name" do expect(resource.command).to eql("foo_resource") end it "sets the resource identity to the command name" do resource.command command expect(resource.identity).to eql(command) end it "warns when the code is not present and a useless `command` is present" do expect(Chef::Log).to receive(:warn).with(/coding error/) expect(Chef::Log).to receive(:warn).with(/deprecated/) resource.code nil resource.command command expect { resource.run_action(:run) }.not_to raise_error end describe "when the code is not present" do let(:code) { nil } it "warns" do expect(Chef::Log).to receive(:warn) expect { resource.run_action(:run) }.not_to raise_error end end end # in Chef-13 the `command` attribute needs to be for internal use only describe "in Chef-13", :chef_gte_13_only do it "should raise an exception when trying to set the command" do expect { resource.command command }.to raise_error # FIXME: add a real error in Chef-13 end it "should initialize the command to nil" do expect(resource.command).to be_nil end describe "when the code is not present" do let(:code) { nil } it "raises an exception" do expect { resource.run_action(:run) }.to raise_error # FIXME: add a real error in Chef-13 expect { resource.run_action(:run) }.not_to raise_error end end end end it "times out when a timeout is set on the resource" do resource.code 'sleep 600' resource.timeout 0.1 expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout) end end chef-12.3.0/spec/functional/resource/cron_spec.rb0000644000004100000410000001455612520074675022000 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Kaustubh Deorukhkar () # 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/base' require 'chef/mixin/shell_out' describe Chef::Resource::Cron, :requires_root, :unix_only do include Chef::Mixin::ShellOut # Platform specific validation routines. def cron_should_exists(cron_name, command) case ohai[:platform] when "aix", "solaris", "opensolaris", "solaris2", "omnios" expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(0) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").stdout.lines.to_a.size).to eq(1) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{command}\"").exitstatus).to eq(0) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{command}\"").stdout.lines.to_a.size).to eq(1) else expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(0) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").stdout.lines.to_a.size).to eq(0) expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{command}\"").exitstatus).to eq(0) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{command}\"").stdout.lines.to_a.size).to eq(0) end end def cron_should_not_exists(cron_name) case ohai[:platform] when "aix", "solaris", "opensolaris", "solaris2", "omnios" expect(shell_out("crontab -l #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(1) expect(shell_out("crontab -l #{new_resource.user} | grep \"#{new_resource.command}\"").stdout.lines.to_a.size).to eq(0) else expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{cron_name}\"").exitstatus).to eq(1) expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{new_resource.command}\"").stdout.lines.to_a.size).to eq(0) end end # Actual tests let(:new_resource) do new_resource = Chef::Resource::Cron.new("Chef functional test cron", run_context) new_resource.user 'root' # @hourly is not supported on solaris, aix if ohai[:platform] == "solaris" || ohai[:platform] == "solaris2" || ohai[:platform] == "aix" new_resource.minute "0 * * * *" else new_resource.minute '@hourly' end new_resource.hour '' new_resource.day '' new_resource.month '' new_resource.weekday '' new_resource.command "/bin/true" new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end describe "create action" do after do new_resource.run_action(:delete) end it "should create a crontab entry" do new_resource.run_action(:create) cron_should_exists(new_resource.name, new_resource.command) end it "should create exactly one crontab entry" do 5.times { new_resource.run_action(:create) } cron_should_exists(new_resource.name, new_resource.command) end end describe "delete action" do before do new_resource.run_action(:create) end it "should delete a crontab entry" do # Note that test cron is created by previous test new_resource.run_action(:delete) cron_should_not_exists(new_resource.name) end end exclude_solaris = ["solaris", "opensolaris", "solaris2", "omnios"].include?(ohai[:platform]) describe "create action with various attributes", :external => exclude_solaris do def create_and_validate_with_attribute(resource, attribute, value) if ohai[:platform] == 'aix' expect {resource.run_action(:create)}.to raise_error(Chef::Exceptions::Cron, /Aix cron entry does not support environment variables. Please set them in script and use script in cron./) else resource.run_action(:create) # Verify if the cron is created successfully cron_attribute_should_exists(resource.name, attribute, value) end end def cron_attribute_should_exists(cron_name, attribute, value) return if ['aix', 'solaris'].include?(ohai[:platform]) # Test if the attribute exists on newly created cron cron_should_exists(cron_name, "") expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{attribute.upcase}=#{value}\"").exitstatus).to eq(0) end after do new_resource.run_action(:delete) end it "should create a crontab entry for mailto attribute" do new_resource.mailto "cheftest@example.com" create_and_validate_with_attribute(new_resource, "mailto", "cheftest@example.com") end it "should create a crontab entry for path attribute" do new_resource.path "/usr/local/bin" create_and_validate_with_attribute(new_resource, "path", "/usr/local/bin") end it "should create a crontab entry for shell attribute" do new_resource.shell "/bin/bash" create_and_validate_with_attribute(new_resource, "shell", "/bin/bash") end it "should create a crontab entry for home attribute" do new_resource.home "/home/opscode" create_and_validate_with_attribute(new_resource, "home", "/home/opscode") end end describe "negative tests for create action" do after do new_resource.run_action(:delete) end def cron_create_should_raise_exception expect { new_resource.run_action(:create) }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/) cron_should_not_exists(new_resource.name) end it "should not create cron with invalid minute" do new_resource.minute "invalid" cron_create_should_raise_exception end it "should not create cron with invalid user" do new_resource.user "1-really-really-invalid-user-name" cron_create_should_raise_exception end end end chef-12.3.0/spec/functional/resource/aix_service_spec.rb0000755000004100000410000000667412520074675023345 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Kaustubh Deorukhkar () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'functional/resource/base' require 'chef/mixin/shell_out' shared_examples "src service" do include Chef::Mixin::ShellOut def service_should_be_started expect(shell_out!("lssrc -a | grep #{new_resource.service_name}").stdout.split(' ').last).to eq("active") end def service_should_be_stopped expect(shell_out!("lssrc -a | grep #{new_resource.service_name}").stdout.split(' ').last).to eq("inoperative") end def get_service_pid args = shell_out!("lssrc -a | grep #{new_resource.service_name}").stdout.split(' ') if args.length == 3 args[1] else args[2] end end describe "start service" do it "should start the service" do new_resource.run_action(:start) service_should_be_started end end describe "stop service" do before do new_resource.run_action(:start) end it "should stop the service" do new_resource.run_action(:stop) service_should_be_stopped end end describe "restart service" do before do new_resource.run_action(:start) end it "should restart the service" do new_resource.run_action(:restart) service_should_be_started end end end describe Chef::Resource::Service, :requires_root, :aix_only do include Chef::Mixin::ShellOut def get_user_id shell_out("id -u #{ENV['USER']}").stdout.chomp end describe "When service is a subsystem" do before(:all) do script_dir = File.join(File.dirname(__FILE__), "/../assets/") shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q") end after(:each) do shell_out("stopsrc -s ctestsys") end after(:all) do shell_out!("rmssys -s ctestsys") end let(:new_resource) do new_resource = Chef::Resource::Service.new("ctestsys", run_context) new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end it_behaves_like "src service" end describe "When service is a group" do before(:all) do script_dir = File.join(File.dirname(__FILE__), "/../assets/") shell_out!("mkssys -s ctestsys -p #{script_dir}/testchefsubsys -u #{get_user_id} -S -n 15 -f 9 -R -Q -G ctestgrp") end after(:each) do shell_out("stopsrc -g ctestgrp") end after(:all) do # rmssys supports only -s option. shell_out!("rmssys -s ctestsys") end let(:new_resource) do new_resource = Chef::Resource::Service.new("ctestgrp", run_context) new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end it_behaves_like "src service" end end chef-12.3.0/spec/functional/resource/link_spec.rb0000644000004100000410000005357312520074675021776 0ustar www-datawww-data# # Author:: John Keiser () # 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 'spec_helper' if windows? require 'chef/win32/file' #probably need this in spec_helper end describe Chef::Resource::Link do let(:file_base) { "file_spec" } let(:expect_updated?) {true} # We create the files in a different directory than tmp to exercise # different file deployment strategies more completely. let(:test_file_dir) do if windows? File.join(ENV['systemdrive'], "test-dir") else File.join(CHEF_SPEC_DATA, "test-dir") end end before do FileUtils::mkdir_p(test_file_dir) end after do FileUtils::rm_rf(test_file_dir) end let(:to) do File.join(test_file_dir, make_tmpname("to_spec")) end let(:target_file) do File.join(test_file_dir, make_tmpname("from_spec")) end after(:each) do begin cleanup_link(to) if File.exists?(to) cleanup_link(target_file) if File.exists?(target_file) cleanup_link(CHEF_SPEC_BACKUP_PATH) if File.exists?(CHEF_SPEC_BACKUP_PATH) rescue puts "Could not remove a file: #{$!}" end end def cleanup_link(path) if windows? && File.directory?(path) # If the link target is a directory rm_rf doesn't work all the # time on windows. system "rmdir '#{path}'" else FileUtils.rm_rf(path) end end def canonicalize(path) windows? ? path.gsub('/', '\\') : path end def symlink(a, b) if windows? Chef::ReservedNames::Win32::File.symlink(a, b) else File.symlink(a, b) end end def symlink?(file) if windows? Chef::ReservedNames::Win32::File.symlink?(file) else File.symlink?(file) end end def readlink(file) if windows? Chef::ReservedNames::Win32::File.readlink(file) else File.readlink(file) end end def link(a, b) if windows? Chef::ReservedNames::Win32::File.link(a, b) else File.link(a, b) end end def create_resource node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(cookbook_repo)) run_context = Chef::RunContext.new(node, cookbook_collection, events) resource = Chef::Resource::Link.new(target_file, run_context) resource.to(to) resource end let(:resource) do create_resource end describe "when supported on platform", :not_supported_on_win2k3 do shared_examples_for 'delete errors out' do it 'delete errors out' do expect { resource.run_action(:delete) }.to raise_error(Chef::Exceptions::Link) expect(File.exist?(target_file) || symlink?(target_file)).to be_truthy end end shared_context 'delete is noop' do describe 'the :delete action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:delete) end it 'leaves the file deleted' do expect(File.exist?(target_file)).to be_falsey expect(symlink?(target_file)).to be_falsey end it 'does not mark the resource updated' do expect(resource).not_to be_updated end it 'does not log that it deleted' do expect(@info.include?("link[#{target_file}] deleted")).to be_falsey end end end shared_context 'delete succeeds' do describe 'the :delete action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:delete) end it 'deletes the file' do expect(File.exist?(target_file)).to be_falsey expect(symlink?(target_file)).to be_falsey end it 'marks the resource updated' do expect(resource).to be_updated end it 'logs that it deleted' do expect(@info.include?("link[#{target_file}] deleted")).to be_truthy end end end shared_context 'create symbolic link succeeds' do describe 'the :create action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:create) end it 'links to the target file' do expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(to)) end it 'marks the resource updated' do expect(resource).to be_updated end it 'logs that it created' do expect(@info.include?("link[#{target_file}] created")).to be_truthy end end end shared_context 'create symbolic link is noop' do describe 'the :create action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:create) end it 'leaves the file linked' do expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(to)) end it 'does not mark the resource updated' do expect(resource).not_to be_updated end it 'does not log that it created' do expect(@info.include?("link[#{target_file}] created")).to be_falsey end end end shared_context 'create hard link succeeds' do describe 'the :create action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:create) end it 'preserves the hard link' do expect(File.exists?(target_file)).to be_truthy expect(symlink?(target_file)).to be_falsey # Writing to one hardlinked file should cause both # to have the new value. expect(IO.read(to)).to eq(IO.read(target_file)) File.open(to, "w") { |file| file.write('wowzers') } expect(IO.read(target_file)).to eq('wowzers') end it 'marks the resource updated' do expect(resource).to be_updated end it 'logs that it created' do expect(@info.include?("link[#{target_file}] created")).to be_truthy end end end shared_context 'create hard link is noop' do describe 'the :create action' do before(:each) do @info = [] allow(Chef::Log).to receive(:info) { |msg| @info << msg } resource.run_action(:create) end it 'links to the target file' do expect(File.exists?(target_file)).to be_truthy expect(symlink?(target_file)).to be_falsey # Writing to one hardlinked file should cause both # to have the new value. expect(IO.read(to)).to eq(IO.read(target_file)) File.open(to, "w") { |file| file.write('wowzers') } expect(IO.read(target_file)).to eq('wowzers') end it 'does not mark the resource updated' do expect(resource).not_to be_updated end it 'does not log that it created' do expect(@info.include?("link[#{target_file}] created")).to be_falsey end end end context "is symbolic" do context 'when the link destination is a file' do before(:each) do File.open(to, "w") do |file| file.write('woohoo') end end context 'and the link does not yet exist' do include_context 'create symbolic link succeeds' include_context 'delete is noop' end context 'and the link already exists and is a symbolic link' do context 'pointing at the target' do before(:each) do symlink(to, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(to)) end include_context 'create symbolic link is noop' include_context 'delete succeeds' it 'the :delete action does not delete the target file' do resource.run_action(:delete) expect(File.exists?(to)).to be_truthy end end context 'pointing somewhere else' do before(:each) do @other_target = File.join(test_file_dir, make_tmpname('other_spec')) File.open(@other_target, 'w') { |file| file.write('eek') } symlink(@other_target, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(@other_target)) end after(:each) do File.delete(@other_target) end include_context 'create symbolic link succeeds' include_context 'delete succeeds' it 'the :delete action does not delete the target file' do resource.run_action(:delete) expect(File.exists?(to)).to be_truthy end end context 'pointing nowhere' do before(:each) do nonexistent = File.join(test_file_dir, make_tmpname('nonexistent_spec')) symlink(nonexistent, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(nonexistent)) end include_context 'create symbolic link succeeds' include_context 'delete succeeds' end end context 'and the link already exists and is a hard link to the file' do before(:each) do link(to, target_file) expect(File.exists?(target_file)).to be_truthy expect(symlink?(target_file)).to be_falsey end include_context 'create symbolic link succeeds' it_behaves_like 'delete errors out' end context 'and the link already exists and is a file' do before(:each) do File.open(target_file, 'w') { |file| file.write('eek') } end include_context 'create symbolic link succeeds' it_behaves_like 'delete errors out' end context 'and the link already exists and is a directory' do before(:each) do Dir.mkdir(target_file) end it 'create errors out' do if windows? expect { resource.run_action(:create) }.to raise_error(Errno::EACCES) elsif os_x? or solaris? or freebsd? or aix? expect { resource.run_action(:create) }.to raise_error(Errno::EPERM) else expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR) end end it_behaves_like 'delete errors out' end context 'and the link already exists and is not writeable to this user', :skip => true do end it_behaves_like 'a securable resource without existing target' do let(:path) { target_file } def allowed_acl(sid, expected_perms) [ ACE.access_allowed(sid, expected_perms[:specific]) ] end def denied_acl(sid, expected_perms) [ ACE.access_denied(sid, expected_perms[:specific]) ] end def parent_inheritable_acls dummy_file_path = File.join(test_file_dir, "dummy_file") dummy_file = FileUtils.touch(dummy_file_path) dummy_desc = get_security_descriptor(dummy_file_path) FileUtils.rm_rf(dummy_file_path) dummy_desc end end end context 'when the link destination is a directory' do before(:each) do Dir.mkdir(to) end # On Windows, readlink fails to open the link. FILE_FLAG_OPEN_REPARSE_POINT # might help, from http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx context 'and the link does not yet exist' do include_context 'create symbolic link succeeds' include_context 'delete is noop' end context 'and the link already exists and points to a different directory' do before(:each) do other_dir = File.join(test_file_dir, make_tmpname("other_dir")) Dir.mkdir(other_dir) symlink(other_dir, target_file) end include_context 'create symbolic link succeeds' end end context "when the link destination is a symbolic link" do context 'to a file that exists' do before(:each) do @other_target = File.join(test_file_dir, make_tmpname("other_spec")) File.open(@other_target, "w") { |file| file.write("eek") } symlink(@other_target, to) expect(symlink?(to)).to be_truthy expect(readlink(to)).to eq(canonicalize(@other_target)) end after(:each) do File.delete(@other_target) end context 'and the link does not yet exist' do include_context 'create symbolic link succeeds' include_context 'delete is noop' end end context 'to a file that does not exist' do before(:each) do @other_target = File.join(test_file_dir, make_tmpname("other_spec")) symlink(@other_target, to) expect(symlink?(to)).to be_truthy expect(readlink(to)).to eq(canonicalize(@other_target)) end context 'and the link does not yet exist' do include_context 'create symbolic link succeeds' include_context 'delete is noop' end end end context "when the link destination is not readable to this user", :skip => true do end context "when the link destination does not exist" do include_context 'create symbolic link succeeds' include_context 'delete is noop' end { '../' => 'with a relative link destination', '' => 'with a bare filename for the link destination' }.each do |prefix, desc| context desc do let(:to) { "#{prefix}#{File.basename(absolute_to)}" } let(:absolute_to) { File.join(test_file_dir, make_tmpname("to_spec")) } before(:each) do resource.to(to) end context 'when the link does not yet exist' do include_context 'create symbolic link succeeds' include_context 'delete is noop' end context 'when the link already exists and points at the target' do before(:each) do symlink(to, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(to)) end include_context 'create symbolic link is noop' include_context 'delete succeeds' end context 'when the link already exists and points at the target with an absolute path' do before(:each) do symlink(absolute_to, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(absolute_to)) end include_context 'create symbolic link succeeds' include_context 'delete succeeds' end end end end context "is a hard link" do before(:each) do resource.link_type(:hard) end context "when the link destination is a file" do before(:each) do File.open(to, "w") do |file| file.write('woohoo') end end context "and the link does not yet exist" do include_context 'create hard link succeeds' include_context 'delete is noop' end context "and the link already exists and is a symbolic link pointing at the same file" do before(:each) do symlink(to, target_file) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(to)) end include_context 'create hard link succeeds' it_behaves_like 'delete errors out' end context 'and the link already exists and is a hard link to the file' do before(:each) do link(to, target_file) expect(File.exists?(target_file)).to be_truthy expect(symlink?(target_file)).to be_falsey end include_context 'create hard link is noop' include_context 'delete succeeds' it 'the :delete action does not delete the target file' do resource.run_action(:delete) expect(File.exists?(to)).to be_truthy end end context "and the link already exists and is a file" do before(:each) do File.open(target_file, 'w') { |file| file.write('tomfoolery') } end include_context 'create hard link succeeds' it_behaves_like 'delete errors out' end context "and the link already exists and is a directory" do before(:each) do Dir.mkdir(target_file) end it 'errors out' do if windows? expect { resource.run_action(:create) }.to raise_error(Errno::EACCES) elsif os_x? or solaris? or freebsd? or aix? expect { resource.run_action(:create) }.to raise_error(Errno::EPERM) else expect { resource.run_action(:create) }.to raise_error(Errno::EISDIR) end end it_behaves_like 'delete errors out' end context "and the link already exists and is not writeable to this user", :skip => true do end context "and specifies security attributes" do before(:each) do resource.owner(windows? ? 'Guest' : 'nobody') end it 'ignores them' do resource.run_action(:create) if windows? expect(Chef::ReservedNames::Win32::Security.get_named_security_info(target_file).owner).not_to eq(SID.Guest) else expect(File.lstat(target_file).uid).not_to eq(Etc.getpwnam('nobody').uid) end end end end context "when the link destination is a directory" do before(:each) do Dir.mkdir(to) end context 'and the link does not yet exist' do it 'create errors out' do expect { resource.run_action(:create) }.to raise_error(windows? ? Chef::Exceptions::Win32APIError : Errno::EPERM) end include_context 'delete is noop' end end context "when the link destination is a symbolic link" do context 'to a real file' do before(:each) do @other_target = File.join(test_file_dir, make_tmpname("other_spec")) File.open(@other_target, "w") { |file| file.write("eek") } symlink(@other_target, to) expect(symlink?(to)).to be_truthy expect(readlink(to)).to eq(canonicalize(@other_target)) end after(:each) do File.delete(@other_target) end context 'and the link does not yet exist' do it 'links to the target file' do resource.run_action(:create) expect(File.exists?(target_file)).to be_truthy # OS X gets angry about this sort of link. Bug in OS X, IMO. pending('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks') if (os_x? or freebsd? or aix?) expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(@other_target)) end include_context 'delete is noop' end end context 'to a nonexistent file' do before(:each) do @other_target = File.join(test_file_dir, make_tmpname("other_spec")) symlink(@other_target, to) expect(symlink?(to)).to be_truthy expect(readlink(to)).to eq(canonicalize(@other_target)) end context 'and the link does not yet exist' do it 'links to the target file' do pending('OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks') if (os_x? or freebsd? or aix?) resource.run_action(:create) # Windows and Unix have different definitions of exists? here, and that's OK. if windows? expect(File.exists?(target_file)).to be_truthy else expect(File.exists?(target_file)).to be_falsey end expect(symlink?(target_file)).to be_truthy expect(readlink(target_file)).to eq(canonicalize(@other_target)) end include_context 'delete is noop' end end end context "when the link destination is not readable to this user", :skip => true do end context "when the link destination does not exist" do context 'and the link does not yet exist' do it 'create errors out' do expect { resource.run_action(:create) }.to raise_error(Errno::ENOENT) end include_context 'delete is noop' end end end end describe "when not supported on platform", :win2k3_only do it "raises error" do expect {resource}.to raise_error(Chef::Exceptions::Win32APIFunctionNotImplemented) end end end chef-12.3.0/spec/functional/resource/windows_service_spec.rb0000644000004100000410000000575412520074675024251 0ustar www-datawww-data# # Author:: Chris Doherty () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do include_context "using Win32::Service" let(:username) { "service_spec_user"} let(:qualified_username) { ".\\#{username}"} let(:password) { "1a2b3c4X!&narf"} let(:user_resource) { r = Chef::Resource::User.new(username, run_context) r.username(username) r.password(password) r.comment("temp spec user") r } let(:global_service_file_path) { "#{ENV['WINDIR']}\\temp\\#{File.basename(test_service[:service_file_path])}" } let(:service_params) { id = "#{$$}_#{rand(1000)}" test_service.merge( { run_as_user: qualified_username, run_as_password: password, service_name: "spec_service_#{id}", service_display_name: "windows_service spec #{id}}", service_description: "Test service for running the windows_service functional spec.", service_file_path: global_service_file_path, } ) } let(:manager) { Chef::Application::WindowsServiceManager.new(service_params) } let(:service_resource) { r = Chef::Resource::WindowsService.new(service_params[:service_name], run_context) [:run_as_user, :run_as_password].each { |prop| r.send(prop, service_params[prop]) } r } before { user_resource.run_action(:create) # the service executable has to be outside the current user's home # directory in order for the logon user to execute it. FileUtils::copy_file(test_service[:service_file_path], global_service_file_path) # if you don't make the file executable by the service user, you'll get # the not-very-helpful "service did not respond fast enough" error. # #mode may break in a post-Windows 8.1 release, and have to be replaced # with the rights stuff in the file resource. file = Chef::Resource::File.new(global_service_file_path, run_context) file.mode("0777") file.run_action(:create) manager.run(%w{--action install}) } after { user_resource.run_action(:remove) manager.run(%w{--action uninstall}) File.delete(global_service_file_path) } describe "logon as a service" do it "successfully runs a service as another user" do service_resource.run_action(:start) end it "raises an exception when it can't grant the logon privilege" end end chef-12.3.0/spec/functional/resource/execute_spec.rb0000644000004100000410000001224512520074675022472 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 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/base' require 'timeout' describe Chef::Resource::Execute do let(:resource) { resource = Chef::Resource::Execute.new("foo_resource", run_context) resource.command("echo hello") resource } describe "when guard is ruby block" do it "guard can still run" do resource.only_if { true } resource.run_action(:run) expect(resource).to be_updated_by_last_action end end describe "when why_run is enabled" do before do Chef::Config[:why_run] = true end let(:guard) { "ruby -e 'exit 0'" } let!(:guard_resource) { interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(resource, guard, nil) interpreter.send(:get_interpreter_resource, resource) } it "executes the guard and not the regular resource" do expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:get_interpreter_resource).and_return(guard_resource) # why_run mode doesn't disable the updated_by_last_action logic, so we really have to look at the provider action # to see if why_run correctly disabled the resource. It should shell_out! for the guard but not the resource. expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).once resource.only_if guard resource.run_action(:run) expect(resource).to be_updated_by_last_action expect(guard_resource).to be_updated_by_last_action end end describe "when parent resource sets :cwd" do let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./nested.json")'} } it "guard inherits :cwd from resource and runs" do resource.cwd CHEF_SPEC_DATA resource.only_if guard resource.run_action(:run) expect(resource).to be_updated_by_last_action end it "guard inherits :cwd from resource and does not run" do resource.cwd CHEF_SPEC_DATA resource.not_if guard resource.run_action(:run) expect(resource).not_to be_updated_by_last_action end end # We use ruby command so that we don't need to deal with platform specific # commands while testing execute resource. We set it so that the resource # will be updated if the ENV variable is set to what we are intending # # FIXME: yeah, but invoking ruby is slow... describe "when parent resource sets :environment" do before do resource.environment({ "SAWS_SECRET" => "supersecret", "SAWS_KEY" => "qwerty", }) end it "guard inherits :environment value from resource and runs" do resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "supersecret"'} resource.run_action(:run) expect(resource).to be_updated_by_last_action end it "guard inherits :environment value from resource and does not run" do resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "supersecret"'} resource.run_action(:run) expect(resource).not_to be_updated_by_last_action end it "guard adds additional values in its :environment and runs" do resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] != "regularsecret"'}, { :environment => { 'SGCE_SECRET' => "regularsecret" } } resource.run_action(:run) expect(resource).to be_updated_by_last_action end it "guard adds additional values in its :environment and does not run" do resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] == "regularsecret"'}, { :environment => { 'SGCE_SECRET' => "regularsecret" } } resource.run_action(:run) expect(resource).not_to be_updated_by_last_action end it "guard overwrites value with its :environment and runs" do resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "regularsecret"'}, { :environment => { 'SAWS_SECRET' => "regularsecret" } } resource.run_action(:run) expect(resource).to be_updated_by_last_action end it "guard overwrites value with its :environment and does not runs" do resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "regularsecret"'}, { :environment => { 'SAWS_SECRET' => "regularsecret" } } resource.run_action(:run) expect(resource).not_to be_updated_by_last_action end end it "times out when a timeout is set on the resource" do Timeout::timeout(5) do resource.command %{ruby -e 'sleep 600'} resource.timeout 0.1 expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout) end end end chef-12.3.0/spec/functional/resource/mount_spec.rb0000644000004100000410000001517312520074675022175 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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 'functional/resource/base' require 'chef/mixin/shell_out' require 'tmpdir' # run this test only for following platforms. include_flag = !(['ubuntu', 'centos', 'aix', 'solaris2'].include?(ohai[:platform])) describe Chef::Resource::Mount, :requires_root, :external => include_flag do include Chef::Mixin::ShellOut # Platform specific setup, cleanup and validation helpers. def setup_device_for_mount # use ramdisk for creating a test device for mount. # This can cleaner if we have chef resource/provider for ramdisk. case ohai[:platform] when "aix" ramdisk = shell_out!("mkramdisk 16M").stdout # identify device, for /dev/rramdisk0 it is /dev/ramdisk0 device = ramdisk.tr("\n","").gsub(/\/rramdisk/, '/ramdisk') fstype = "jfs2" shell_out!("mkfs -V #{fstype} #{device}") when "ubuntu", "centos" device = "/dev/ram1" shell_out("ls -1 /dev/ram*").stdout.each_line do |d| if shell_out("mount | grep #{d}").exitstatus == "1" # this device is not mounted, so use it. device = d break end end fstype = "tmpfs" shell_out!("mkfs -q #{device} 512") when "solaris2" device = "swap" fstype = "tmpfs" else end [device, fstype] end def cleanup_device(device) case ohai[:platform] when "aix" ramdisk = device.gsub(/\/ramdisk/, '/rramdisk') shell_out("rmramdisk #{ramdisk}") else end end def cleanup_mount(mount_point) shell_out("umount #{mount_point}") end # platform specific validations. def mount_should_exist(mount_point, device, fstype = nil, options = nil) validation_cmd = "mount | grep #{mount_point} | grep #{device} " validation_cmd << " | grep #{fstype} " unless fstype.nil? validation_cmd << " | grep #{options.join(',')} " unless options.nil? || options.empty? expect(shell_out(validation_cmd).exitstatus).to eq(0) end def mount_should_not_exists(mount_point) expect(shell_out("mount").stdout).not_to include(mount_point) end def unix_mount_config_file case ohai[:platform] when 'aix' mount_config = "/etc/filesystems" when 'solaris2' mount_config = "/etc/vfstab" else mount_config = "/etc/fstab" end end def mount_should_be_enabled(mount_point, device) case ohai[:platform] when 'aix' expect(shell_out("cat #{unix_mount_config_file} | grep \"#{mount_point}:\" ").exitstatus).to eq(0) else expect(shell_out("cat #{unix_mount_config_file} | grep \"#{mount_point}\" | grep \"#{device}\" ").exitstatus).to eq(0) end end def mount_should_be_disabled(mount_point) expect(shell_out("cat #{unix_mount_config_file}").stdout).not_to include("#{mount_point}:") end let(:new_resource) do new_resource = Chef::Resource::Mount.new(@mount_point, run_context) new_resource.device @device new_resource.name @mount_point new_resource.fstype @fstype new_resource.options "log=NULL" if ohai[:platform] == 'aix' new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end let(:current_resource) do provider.load_current_resource provider.current_resource end # Actual tests begin here. before(:all) do @device, @fstype = setup_device_for_mount @mount_point = Dir.mktmpdir("testmount") # Make sure all the potentially leaked mounts are cleared up shell_out("mount").stdout.each_line do |line| if line.include? "testmount" line.split(" ").each do |section| cleanup_mount(section) if section.include? "testmount" end end end end after(:all) do Dir.rmdir(@mount_point) cleanup_device(@device) end after(:each) do cleanup_mount(new_resource.mount_point) end describe "when the target state is a mounted filesystem" do it "should mount the filesystem if it isn't mounted" do expect(current_resource.enabled).to be_falsey expect(current_resource.mounted).to be_falsey new_resource.run_action(:mount) expect(new_resource).to be_updated mount_should_exist(new_resource.mount_point, new_resource.device) end end # don't run the remount tests on solaris2 (tmpfs does not support remount) # Need to make sure the platforms we've already excluded are considered: skip_remount = include_flag || (ohai[:platform] == "solaris2") describe "when the filesystem should be remounted and the resource supports remounting", :external => skip_remount do it "should remount the filesystem if it is mounted" do new_resource.run_action(:mount) mount_should_exist(new_resource.mount_point, new_resource.device) new_resource.supports[:remount] = true new_resource.options "rw,log=NULL" if ohai[:platform] == 'aix' new_resource.run_action(:remount) mount_should_exist(new_resource.mount_point, new_resource.device, nil, (ohai[:platform] == 'aix') ? new_resource.options : nil) end end describe "when the target state is a unmounted filesystem" do it "should umount the filesystem if it is mounted" do new_resource.run_action(:mount) mount_should_exist(new_resource.mount_point, new_resource.device) new_resource.run_action(:umount) mount_should_not_exists(new_resource.mount_point) end end describe "when enabling the filesystem to be mounted" do after do new_resource.run_action(:disable) end it "should enable the mount if it isn't enable" do new_resource.run_action(:mount) new_resource.run_action(:enable) mount_should_be_enabled(new_resource.mount_point, new_resource.device) end end describe "when the target state is to disable the mount" do it "should disable the mount if it is enabled" do new_resource.run_action(:mount) new_resource.run_action(:enable) new_resource.run_action(:disable) mount_should_be_disabled(new_resource.mount_point) end end end chef-12.3.0/spec/functional/resource/env_spec.rb0000755000004100000410000001752012520074675021624 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 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::Env, :windows_only do context 'when running on Windows' do let(:chef_env_test_lower_case) { 'chefenvtest' } let(:chef_env_test_mixed_case) { 'chefENVtest' } let(:env_dne_key) { 'env_dne_key' } let(:env_value1) { 'value1' } let(:env_value2) { 'value2' } let(:env_value_expandable) { '%SystemRoot%' } let(:test_run_context) { node = Chef::Node.new node.default['os'] = 'windows' node.default['platform'] = 'windows' node.default['platform_version'] = '6.1' empty_events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, empty_events) } let(:test_resource) { Chef::Resource::Env.new('unknown', test_run_context) } before(:each) do resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context) resource_lower.run_action(:delete) resource_mixed = Chef::Resource::Env.new(chef_env_test_mixed_case, test_run_context) resource_mixed.run_action(:delete) end context "when the create action is invoked" do it 'should create an environment variable for action create' do expect(ENV[chef_env_test_lower_case]).to eq(nil) test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) end it "should modify an existing variable's value to a new value" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.value(env_value2) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value2) end it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.key_name(chef_env_test_mixed_case) test_resource.value(env_value2) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value2) end it 'should not expand environment variables if the variable is not PATH' do expect(ENV[chef_env_test_lower_case]).to eq(nil) test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value_expandable) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable) end end context "when the modify action is invoked" do it "should raise an exception for modify if the variable doesn't exist" do expect(ENV[chef_env_test_lower_case]).to eq(nil) test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) expect {test_resource.run_action(:modify) }.to raise_error(Chef::Exceptions::Env) end it "should modify an existing variable's value to a new value" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.value(env_value2) test_resource.run_action(:modify) expect(ENV[chef_env_test_lower_case]).to eq(env_value2) end # This examlpe covers Chef Issue #1754 it "should modify an existing variable's value to a new value if the variable name case differs from the existing variable" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.key_name(chef_env_test_mixed_case) test_resource.value(env_value2) test_resource.run_action(:modify) expect(ENV[chef_env_test_lower_case]).to eq(env_value2) end it 'should not expand environment variables if the variable is not PATH' do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.value(env_value_expandable) test_resource.run_action(:modify) expect(ENV[chef_env_test_lower_case]).to eq(env_value_expandable) end context 'when using PATH' do let(:random_name) { Time.now.to_i } let(:env_val) { "#{env_value_expandable}_#{random_name}"} let!(:path_before) { test_resource.provider_for_action(test_resource.action).env_value('PATH') || '' } let!(:env_path_before) { ENV['PATH'] } it 'should expand PATH' do expect(path_before).not_to include(env_val) test_resource.key_name('PATH') test_resource.value("#{path_before};#{env_val}") test_resource.run_action(:create) expect(ENV['PATH']).not_to include(env_val) expect(ENV['PATH']).to include("#{random_name}") end after(:each) do # cleanup so we don't flood the path test_resource.key_name('PATH') test_resource.value(path_before) test_resource.run_action(:create) ENV['PATH'] = env_path_before end end end context "when the delete action is invoked" do it "should delete an environment variable" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.run_action(:delete) expect(ENV[chef_env_test_lower_case]).to eq(nil) end it "should not raise an exception when a non-existent environment variable is deleted" do expect(ENV[chef_env_test_lower_case]).to eq(nil) test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) expect{test_resource.run_action(:delete)}.not_to raise_error expect(ENV[chef_env_test_lower_case]).to eq(nil) end it "should delete an existing variable's value to a new value if the specified variable name case differs from the existing variable" do test_resource.key_name(chef_env_test_lower_case) test_resource.value(env_value1) test_resource.run_action(:create) expect(ENV[chef_env_test_lower_case]).to eq(env_value1) test_resource.key_name(chef_env_test_mixed_case) test_resource.run_action(:delete) expect(ENV[chef_env_test_lower_case]).to eq(nil) expect(ENV[chef_env_test_mixed_case]).to eq(nil) end it "should delete a value from the current process even if it is not in the registry" do expect(ENV[env_dne_key]).to eq(nil) ENV[env_dne_key] = env_value1 test_resource.key_name(env_dne_key) test_resource.run_action(:delete) expect(ENV[env_dne_key]).to eq(nil) end end end end chef-12.3.0/spec/functional/resource/ifconfig_spec.rb0000644000004100000410000001121412520074675022607 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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 'functional/resource/base' require 'chef/mixin/shell_out' # run this test only for following platforms. include_flag = !(['ubuntu', 'centos', 'aix'].include?(ohai[:platform])) describe Chef::Resource::Ifconfig, :requires_root, :external => include_flag do include Chef::Mixin::ShellOut let(:new_resource) do new_resource = Chef::Resource::Ifconfig.new('10.10.0.1', run_context) new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end let(:current_resource) do provider.load_current_resource end def lo_interface_for_test # use loopback interface for tests case ohai[:platform] when "aix" 'lo0' else 'lo' end end # **Caution: any updates to core interfaces can be risky. def en0_interface_for_test case ohai[:platform] when "aix" 'en0' else 'eth0' end end def network_interface_alias(interface) case ohai[:platform] when "aix" interface else interface + ":10" end end # platform specific test setup and validation routines def setup_add_interface(resource) resource.device network_interface_alias(en0_interface_for_test) end def setup_enable_interface(resource) resource.device network_interface_alias(en0_interface_for_test) end def interface_should_exists(interface) expect(shell_out("ifconfig #{@interface} | grep 10.10.0.1").exitstatus).to eq(0) end def interface_should_not_exists(interface) expect(shell_out("ifconfig #{@interface} | grep 10.10.0.1").exitstatus).to eq(1) end def interface_persistence_should_exists(interface) case ohai[:platform] when "aix" expect(shell_out("lsattr -E -l #{@interface} | grep 10.10.0.1").exitstatus).to eq(0) else end end def interface_persistence_should_not_exists(interface) case ohai[:platform] when "aix" expect(shell_out("lsattr -E -l #{@interface} | grep 10.10.0.1").exitstatus).to eq(1) else end end # Actual tests describe "#load_current_resource" do it 'should load given interface' do new_resource.device lo_interface_for_test expect(current_resource.device).to eql(lo_interface_for_test) expect(current_resource.inet_addr).to match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) end end exclude_test = ohai[:platform] != 'ubuntu' describe "#action_add", :external => exclude_test do after do new_resource.run_action(:delete) end it "should add interface (vip)" do setup_add_interface(new_resource) new_resource.run_action(:add) interface_should_exists(network_interface_alias(en0_interface_for_test)) interface_persistence_should_exists(network_interface_alias(en0_interface_for_test)) end end describe "#action_enable", :external => exclude_test do after do new_resource.run_action(:disable) end it "should enable interface (vip)" do setup_enable_interface(new_resource) new_resource.run_action(:enable) interface_should_exists(network_interface_alias(en0_interface_for_test)) end end describe "#action_disable", :external => exclude_test do before do setup_enable_interface(new_resource) new_resource.run_action(:enable) end it "should disable interface (vip)" do new_resource.run_action(:disable) expect(new_resource).to be_updated_by_last_action interface_should_not_exists(network_interface_alias(en0_interface_for_test)) end end describe "#action_delete", :external => exclude_test do before do setup_add_interface(new_resource) new_resource.run_action(:add) end it "should delete interface (vip)" do new_resource.run_action(:delete) expect(new_resource).to be_updated_by_last_action interface_should_not_exists(network_interface_alias(en0_interface_for_test)) interface_persistence_should_not_exists(network_interface_alias(en0_interface_for_test)) end end end chef-12.3.0/spec/functional/resource/package_spec.rb0000644000004100000410000003104712520074675022424 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Daniel DeLeo () # 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 'webrick' module AptServer def enable_testing_apt_source File.open("/etc/apt/sources.list.d/chef-integration-test.list", "w+") do |f| f.puts "deb http://localhost:9000/ sid main" end # Magic to update apt cache for only our repo shell_out!("apt-get update " + '-o Dir::Etc::sourcelist="sources.list.d/chef-integration-test.list" ' + '-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"') end def disable_testing_apt_source FileUtils.rm("/etc/apt/sources.list.d/chef-integration-test.list") rescue Errno::ENOENT puts("Attempted to remove integration test from /etc/apt/sources.list.d but it didn't exist") end def tcp_test_port(hostname, port) tcp_socket = TCPSocket.new(hostname, port) true rescue Errno::ETIMEDOUT false rescue Errno::ECONNREFUSED false ensure tcp_socket && tcp_socket.close end def apt_server @apt_server ||= WEBrick::HTTPServer.new( :Port => 9000, :DocumentRoot => apt_data_dir + "/var/www/apt", # Make WEBrick quiet, comment out for debug. :Logger => Logger.new(StringIO.new), :AccessLog => [ StringIO.new, WEBrick::AccessLog::COMMON_LOG_FORMAT ] ) end def run_apt_server apt_server.start end def start_apt_server @apt_server_thread = Thread.new do run_apt_server end until tcp_test_port("localhost", 9000) do if @apt_server_thread.alive? sleep 1 else @apt_server_thread.join raise "apt server failed to start" end end end def stop_apt_server apt_server.shutdown @apt_server_thread.join end def apt_data_dir File.join(CHEF_SPEC_DATA, "apt") end end metadata = { :unix_only => true, :requires_root => true, :provider => {:package => Chef::Provider::Package::Apt}, :arch => "x86_64" # test packages are 64bit } describe Chef::Resource::Package, metadata do include Chef::Mixin::ShellOut context "with a remote package source" do include AptServer before(:all) do # Disable mixlib-shellout live streams Chef::Log.level = :warn start_apt_server enable_testing_apt_source end after(:all) do stop_apt_server disable_testing_apt_source shell_out!("apt-get clean") end after do shell_out!("dpkg -r chef-integration-test") shell_out("dpkg --clear-avail") shell_out!("apt-get clean") end let(:node) do n = Chef::Node.new n.consume_external_attrs(OHAI_SYSTEM.data.dup, {}) n end let(:events) do Chef::EventDispatch::Dispatcher.new end # TODO: lots of duplication from client.rb; # All of this must be setup for preseed files to get found let(:cookbook_collection) do cookbook_path = File.join(CHEF_SPEC_DATA, "cookbooks") cl = Chef::CookbookLoader.new(cookbook_path) cl.load_cookbooks Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_path) Chef::CookbookCollection.new(cl) end let(:run_context) do Chef::RunContext.new(node, cookbook_collection, events) end def base_resource r = Chef::Resource::Package.new("chef-integration-test", run_context) # The apt repository in the spec data is not gpg signed, so we need to # force apt to accept the package: r.options("--force-yes") r end let(:package_resource) do base_resource end context "when the package is not yet installed" do it "installs the package with action :install" do package_resource.run_action(:install) shell_out!("dpkg -l chef-integration-test") expect(package_resource).to be_updated_by_last_action end it "installs the package for action :upgrade" do package_resource.run_action(:upgrade) shell_out!("dpkg -l chef-integration-test") expect(package_resource).to be_updated_by_last_action end it "does nothing for action :remove" do package_resource.run_action(:remove) shell_out!("dpkg -l chef-integration-test", :returns => [1]) expect(package_resource).not_to be_updated_by_last_action end it "does nothing for action :purge" do package_resource.run_action(:purge) shell_out!("dpkg -l chef-integration-test", :returns => [1]) expect(package_resource).not_to be_updated_by_last_action end context "and a not-available package version is specified" do let(:package_resource) do r = base_resource r.version("2.0") r end it "raises a reasonable error for action :install" do expect do package_resource.run_action(:install) end.to raise_error(Mixlib::ShellOut::ShellCommandFailed) end end describe "when preseeding the install" do let(:file_cache_path) { Dir.mktmpdir } before do Chef::Config[:file_cache_path] = file_cache_path debconf_reset = 'chef-integration-test chef-integration-test/sample-var string "INVALID"' shell_out!("echo #{debconf_reset} |debconf-set-selections") end after do FileUtils.rm_rf(file_cache_path) end context "with a preseed file" do let(:package_resource) do r = base_resource r.cookbook_name = "preseed" r.response_file("preseed-file.seed") r end it "preseeds the package, then installs it" do package_resource.run_action(:install) cmd = shell_out!("debconf-show chef-integration-test") expect(cmd.stdout).to include('chef-integration-test/sample-var: "hello world"') expect(package_resource).to be_updated_by_last_action end context "and the preseed file exists and is up-to-date" do before do # Code here is duplicated from the implementation. Not great, but # it should at least fail if the code gets out of sync. source = File.join(CHEF_SPEC_DATA, "cookbooks/preseed/files/default/preseed-file.seed") file_cache_dir = Chef::FileCache.create_cache_path("preseed/preseed") dest = "#{file_cache_dir}/chef-integration-test-1.1-1.seed" FileUtils.cp(source, dest) end it "does not update the package configuration" do package_resource.run_action(:install) cmd = shell_out!("debconf-show chef-integration-test") expect(cmd.stdout).to include('chef-integration-test/sample-var: INVALID') expect(package_resource).to be_updated_by_last_action end end end context "with a preseed template" do # NOTE: in the fixtures, there is also a cookbook_file named # "preseed-template.seed". This implicitly tests that templates are # preferred over cookbook_files when both are present. let(:package_resource) do r = base_resource r.cookbook_name = "preseed" r.response_file("preseed-template.seed") r end before do node.set[:preseed_value] = "FROM TEMPLATE" end it "preseeds the package, then installs it" do package_resource.run_action(:install) cmd = shell_out!("debconf-show chef-integration-test") expect(cmd.stdout).to include('chef-integration-test/sample-var: "FROM TEMPLATE"') expect(package_resource).to be_updated_by_last_action end context "with variables" do let(:package_resource) do r = base_resource r.cookbook_name = "preseed" r.response_file("preseed-template-variables.seed") r.response_file_variables({ :template_variable => 'SUPPORTS VARIABLES' }) r end it "preseeds the package, then installs it" do package_resource.run_action(:install) cmd = shell_out!("debconf-show chef-integration-test") expect(cmd.stdout).to include('chef-integration-test/sample-var: "SUPPORTS VARIABLES"') expect(package_resource).to be_updated_by_last_action end end end end # installing w/ preseed end # when package not installed context "and the desired version of the package is installed" do before do v_1_1_package = File.expand_path("apt/chef-integration-test_1.1-1_amd64.deb", CHEF_SPEC_DATA) shell_out!("dpkg -i #{v_1_1_package}") end it "does nothing for action :install" do package_resource.run_action(:install) shell_out!("dpkg -l chef-integration-test", :returns => [0]) expect(package_resource).not_to be_updated_by_last_action end it "does nothing for action :upgrade" do package_resource.run_action(:upgrade) shell_out!("dpkg -l chef-integration-test", :returns => [0]) expect(package_resource).not_to be_updated_by_last_action end # Verify that the package is removed by running `dpkg -l PACKAGE` # On Ubuntu 12.10 and newer, the command exits 1. # # On Ubuntu 12.04 and older, the `dpkg -l` command will exit 0 and # display a package status message like this: # # Desired=Unknown/Install/Remove/Purge/Hold # | Status=Not/Inst/Cfg-files/Unpacked/Failed-cfg/Half-inst/trig-aWait/Trig-pend # |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) # ||/ Name Version Description # +++-=================================-=========================================-============================================ # un chef-integration-test (no description available) def pkg_should_be_removed # will raise if exit code != 0,1 pkg_check = shell_out!("dpkg -l chef-integration-test", :returns => [0,1]) if pkg_check.exitstatus == 0 expect(pkg_check.stdout).to match(/un[\s]+chef-integration-test/) end end it "removes the package for action :remove" do package_resource.run_action(:remove) pkg_should_be_removed expect(package_resource).to be_updated_by_last_action end it "removes the package for action :purge" do package_resource.run_action(:purge) pkg_should_be_removed expect(package_resource).to be_updated_by_last_action end end context "and an older version of the package is installed" do before do v_1_0_package = File.expand_path("apt/chef-integration-test_1.0-1_amd64.deb", CHEF_SPEC_DATA) shell_out!("dpkg -i #{v_1_0_package}") end it "does nothing for action :install" do package_resource.run_action(:install) shell_out!("dpkg -l chef-integration-test", :returns => [0]) expect(package_resource).not_to be_updated_by_last_action end it "upgrades the package for action :upgrade" do package_resource.run_action(:upgrade) dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0]) expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/) expect(package_resource).to be_updated_by_last_action end context "and the resource specifies the new version" do let(:package_resource) do r = base_resource r.version("1.1-1") r end it "upgrades the package for action :install" do package_resource.run_action(:install) dpkg_l = shell_out!("dpkg -l chef-integration-test", :returns => [0]) expect(dpkg_l.stdout).to match(/chef\-integration\-test[\s]+1\.1\-1/) expect(package_resource).to be_updated_by_last_action end end end end end chef-12.3.0/spec/functional/resource/cookbook_file_spec.rb0000644000004100000410000000523412520074675023635 0ustar www-datawww-data# # Author:: Tim Hinderliter () # 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::Resource::CookbookFile do include_context Chef::Resource::File let(:file_base) { 'cookbook_file_spec' } let(:source) { 'java.response' } let(:cookbook_name) { 'java' } let(:expected_content) do content = File.open(File.join(CHEF_SPEC_DATA, 'cookbooks', 'java', 'files', 'default', 'java.response'), "rb") do |f| f.read end content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding) content end let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a securable resource with reporting" def create_resource # set up cookbook collection for this run to use, based on our # spec data. cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, 'cookbooks')) Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) loader = Chef::CookbookLoader.new(cookbook_repo) loader.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(loader) node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, cookbook_collection, events) resource = Chef::Resource::CookbookFile.new(path, run_context) resource.cookbook(cookbook_name) resource.source(source) resource end let(:resource) do create_resource end it_behaves_like "a file resource" # These examples cover CHEF-3467 where unexpected and incorrect # permissions can result on Windows because CookbookFile's # implementation # stages files in temp. context "targets a file outside of the system temp directory" do let(:windows_non_temp_dir) { File.join(ENV['systemdrive'], make_tmpname(file_base, "non-temp")) } let(:path) { File.join(windows_non_temp_dir, make_tmpname(file_base)) } before do FileUtils::mkdir_p(windows_non_temp_dir) if Chef::Platform.windows? end after do FileUtils.rm_r(windows_non_temp_dir) if Chef::Platform.windows? && File.exists?(windows_non_temp_dir) end end end chef-12.3.0/spec/functional/resource/directory_spec.rb0000644000004100000410000000235212520074675023032 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' describe Chef::Resource::Directory do include_context Chef::Resource::Directory let(:directory_base) { "directory_spec" } let(:default_mode) { (0777 & ~File.umask).to_s(8) } def create_resource events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new run_context = Chef::RunContext.new(node, {}, events) Chef::Resource::Directory.new(path, run_context) end let(:resource) do create_resource end it_behaves_like "a directory resource" it_behaves_like "a securable resource with reporting" end chef-12.3.0/spec/functional/resource/remote_directory_spec.rb0000644000004100000410000001527312520074675024413 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' describe Chef::Resource::RemoteDirectory do include_context Chef::Resource::Directory let(:directory_base) { "directory_spec" } let(:default_mode) { (0777 & ~File.umask).to_s(8) } def create_resource cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) node = Chef::Node.new cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, cookbook_collection, events) resource = Chef::Resource::RemoteDirectory.new(path, run_context) resource.source "remotedir" resource.cookbook('openldap') resource end def create_extraneous_files FileUtils.mkdir_p(File.join(path, 'remotesubdir')) @existing1 = File.join(path, 'marked_for_death.txt') @existing2 = File.join(path, 'remotesubdir', 'marked_for_death_again.txt') FileUtils.touch(@existing1) FileUtils.touch(@existing2) end let(:resource) do create_resource end let(:resource_second_pass) do create_resource end # See spec/data/cookbooks/openldap/files/default let(:expected_files) do [ File.join(path, 'remote_dir_file1.txt'), File.join(path, 'remote_dir_file2.txt'), File.join(path, 'remotesubdir', 'remote_subdir_file1.txt'), File.join(path, 'remotesubdir', 'remote_subdir_file2.txt'), File.join(path, 'remotesubdir', '.a_dotfile'), File.join(path, '.a_dotdir', '.a_dotfile_in_a_dotdir') ] end it_behaves_like "a directory resource" it_behaves_like "a securable resource with reporting" context "when creating the remote directory with purging disabled" do context "and the directory does not yet exist" do before do resource.run_action(:create) end it "transfers the directory with all contents" do expected_files.each do |file_path| expect(File).to exist(file_path) end end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end context "and there are extraneous files in the directory" do before do create_extraneous_files resource.run_action(:create) end it "does not modify the expected state of the directory" do expected_files.each do |file_path| expect(File).to exist(file_path) end end it "does not remove unmanaged files" do expect(File).to exist(@existing1) expect(File).to exist(@existing2) end end context "and the directory is in the desired state" do before do resource.run_action(:create) resource_second_pass.run_action(:create) end it "does not modify the expected state of the directory" do expected_files.each do |file_path| expect(File).to exist(file_path) end end it "is not marked as updated by last action" do expect(resource_second_pass).not_to be_updated_by_last_action end end describe "with overwrite disabled" do before(:each) do resource.purge(false) resource.overwrite(false) end it "leaves modifications alone" do FileUtils.mkdir_p(File.join(path, 'remotesubdir')) modified_file = File.join(path, 'remote_dir_file1.txt') modified_subdir_file = File.join(path, 'remotesubdir', 'remote_subdir_file1.txt') File.open(modified_file, 'a') {|f| f.puts "santa is real"} File.open(modified_subdir_file, 'a') {|f| f.puts "so is rudolph"} modified_file_checksum = sha256_checksum(modified_file) modified_subdir_file_checksum = sha256_checksum(modified_subdir_file) resource.run_action(:create) expect(sha256_checksum(modified_file)).to eq(modified_file_checksum) expect(sha256_checksum(modified_subdir_file)).to eq(modified_subdir_file_checksum) end end end context "when creating the directory with purging enabled" do before(:each) do resource.purge(true) end context "and there are no extraneous files in the directory" do before do resource.run_action(:create) end it "creates the directory contents as normal" do expected_files.each do |file_path| expect(File).to exist(file_path) end end end context "and there are extraneous files in the directory" do before do create_extraneous_files resource.run_action(:create) end it "removes unmanaged files" do expect(File).not_to exist(@existing1) expect(File).not_to exist(@existing2) end it "does not modify managed files" do expected_files.each do |file_path| expect(File).to exist(file_path) end end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end context "and there are deeply nested extraneous files in the directory" do before do FileUtils.mkdir_p(File.join(path, 'a', 'multiply', 'nested', 'directory')) @existing1 = File.join(path, 'a', 'foo.txt') @existing2 = File.join(path, 'a', 'multiply', 'bar.txt') @existing3 = File.join(path, 'a', 'multiply', 'nested', 'baz.txt') @existing4 = File.join(path, 'a', 'multiply', 'nested', 'directory', 'qux.txt') FileUtils.touch(@existing1) FileUtils.touch(@existing2) FileUtils.touch(@existing3) FileUtils.touch(@existing4) resource.run_action(:create) end it "removes files in subdirectories before files above" do expect(File).not_to exist(@existing1) expect(File).not_to exist(@existing2) expect(File).not_to exist(@existing3) expect(File).not_to exist(@existing4) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end end end chef-12.3.0/spec/functional/resource/user/0000755000004100000410000000000012520074675020443 5ustar www-datawww-datachef-12.3.0/spec/functional/resource/user/useradd_spec.rb0000644000004100000410000005031512520074675023435 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Daniel DeLeo () # 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/base' require 'chef/mixin/shell_out' def user_provider_for_platform case ohai[:platform] when "aix" Chef::Provider::User::Aix else Chef::Provider::User::Useradd end end metadata = { :unix_only => true, :requires_root => true, :not_supported_on_mac_osx => true, :provider => {:user => user_provider_for_platform} } describe Chef::Provider::User::Useradd, metadata do include Chef::Mixin::ShellOut # Utility code for /etc/passwd interaction, avoid any caching of user records: PwEntry = Struct.new(:name, :passwd, :uid, :gid, :gecos, :home, :shell) class UserNotFound < StandardError; end def pw_entry passwd_file = File.open("/etc/passwd", "rb") {|f| f.read} matcher = /^#{Regexp.escape(username)}.+$/ if passwd_entry = passwd_file.scan(matcher).first PwEntry.new(*passwd_entry.split(':')) else raise UserNotFound, "no entry matching #{matcher.inspect} found in /etc/passwd" end end def etc_shadow case ohai[:platform] when "aix" File.open("/etc/security/passwd") {|f| f.read } else File.open("/etc/shadow") {|f| f.read } end end def supports_quote_in_username? OHAI_SYSTEM["platform_family"] == "debian" end def password_should_be_set if ohai[:platform] == "aix" expect(pw_entry.passwd).to eq("!") else expect(pw_entry.passwd).to eq("x") end end def try_cleanup ['/home/cheftestfoo', '/home/cheftestbar'].each do |f| FileUtils.rm_rf(f) if File.exists? f end ['cf-test'].each do |u| r = Chef::Resource::User.new("DELETE USER", run_context) r.username('cf-test') r.run_action(:remove) end end before do # Silence shell_out live stream Chef::Log.level = :warn try_cleanup end after do max_retries = 3 while max_retries > 0 begin pw_entry # will raise if the user doesn't exist status = shell_out!("userdel", "-r", username, :returns => [0,8,12]) # Error code 8 during userdel indicates that the user is logged in. # This occurs randomly because the accounts daemon holds a lock due to which userdel fails. # The work around is to retry userdel for 3 times. break if status.exitstatus != 8 sleep 1 max_retries = max_retries -1 rescue UserNotFound break end end status.error! if max_retries == 0 end let(:node) do n = Chef::Node.new n.consume_external_attrs(OHAI_SYSTEM.data.dup, {}) n end let(:events) do Chef::EventDispatch::Dispatcher.new end let(:run_context) do Chef::RunContext.new(node, {}, events) end let(:username) do "cf-test" end let(:uid) { nil } let(:home) { nil } let(:manage_home) { false } let(:password) { nil } let(:system) { false } let(:comment) { nil } let(:user_resource) do r = Chef::Resource::User.new("TEST USER RESOURCE", run_context) r.username(username) r.uid(uid) r.home(home) r.comment(comment) r.manage_home(manage_home) r.password(password) r.system(system) r end let(:expected_shadow) do if ohai[:platform] == "aix" expected_shadow = "cf-test" # For aix just check user entry in shadow file else expected_shadow = "cf-test:$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end let(:skip) { false } describe "action :create" do context "when the user does not exist beforehand" do before do if reason = skip pending(reason) end user_resource.run_action(:create) expect(user_resource).to be_updated_by_last_action end it "ensures the user exists" do expect(pw_entry.name).to eq(username) end # On Debian, the only constraints are that usernames must neither start # with a dash ('-') nor plus ('+') nor tilde ('~') nor contain a colon # (':'), a comma (','), or a whitespace (space: ' ', end of line: '\n', # tabulation: '\t', etc.). Note that using a slash ('/') may break the # default algorithm for the definition of the user's home directory. context "and the username contains a single quote" do let(:skip) do if supports_quote_in_username? false else "Platform #{OHAI_SYSTEM["platform"]} not expected to support username w/ quote" end end let(:username) { "t'bilisi" } it "ensures the user exists" do expect(pw_entry.name).to eq(username) end end context "when uid is set" do # Should verify uid not in use... let(:uid) { 1999 } it "ensures the user has the given uid" do expect(pw_entry.uid).to eq("1999") end end context "when comment is set" do let(:comment) { "hello this is dog" } it "ensures the comment is set" do expect(pw_entry.gecos).to eq("hello this is dog") end context "in standard gecos format" do let(:comment) { "Bobo T. Clown,some building,555-555-5555,@boboclown" } it "ensures the comment is set" do expect(pw_entry.gecos).to eq(comment) end end context "to a string containing multibyte characters" do let(:comment) { "(╯°□°)╯︵ ┻━┻" } it "ensures the comment is set" do actual = pw_entry.gecos actual.force_encoding(Encoding::UTF_8) if "".respond_to?(:force_encoding) expect(actual).to eq(comment) end end context "to a string containing an apostrophe `'`" do let(:comment) { "don't go" } it "ensures the comment is set" do expect(pw_entry.gecos).to eq(comment) end end end context "when home is set" do let(:home) { "/home/#{username}" } it "ensures the user's home is set to the given path" do expect(pw_entry.home).to eq("/home/#{username}") end if %w{rhel fedora}.include?(OHAI_SYSTEM["platform_family"]) # Inconsistent behavior. See: CHEF-2205 it "creates the home dir when not explicitly asked to on RHEL (XXX)" do expect(File).to exist("/home/#{username}") end else it "does not create the home dir without `manage_home'" do expect(File).not_to exist("/home/#{username}") end end context "and manage_home is enabled" do let(:manage_home) { true } it "ensures the user's home directory exists" do expect(File).to exist("/home/#{username}") end end end context "when a password is specified" do # openssl passwd -1 "secretpassword" let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end it "sets the user's shadow password" do password_should_be_set expect(etc_shadow).to include(expected_shadow) end end context "when a system user is specified" do let(:system) { true } let(:uid_min) do case ohai[:platform] when "aix" # UIDs and GIDs below 100 are typically reserved for system accounts and services # http://www.ibm.com/developerworks/aix/library/au-satuidgid/ 100 else # from `man useradd`, login user means uid will be between # UID_SYS_MIN and UID_SYS_MAX defined in /etc/login.defs. On my # Ubuntu 13.04 system, these are commented out, so we'll look at # UID_MIN to find the lower limit of the non-system-user range, and # use that value in our assertions. login_defs = File.open("/etc/login.defs", "rb") {|f| f.read } uid_min_scan = /^UID_MIN\s+(\d+)/ login_defs.match(uid_min_scan)[1] end end it "ensures the user has the properties of a system user" do expect(pw_entry.uid.to_i).to be < uid_min.to_i end end end # when the user does not exist beforehand context "when the user already exists" do let(:expect_updated?) { true } let(:existing_uid) { nil } let(:existing_home) { nil } let(:existing_manage_home) { false } let(:existing_password) { nil } let(:existing_system) { false } let(:existing_comment) { nil } let(:existing_user) do r = Chef::Resource::User.new("TEST USER RESOURCE", run_context) # username is identity attr, must match. r.username(username) r.uid(existing_uid) r.home(existing_home) r.comment(existing_comment) r.manage_home(existing_manage_home) r.password(existing_password) r.system(existing_system) r end before do if reason = skip pending(reason) end existing_user.run_action(:create) expect(existing_user).to be_updated_by_last_action user_resource.run_action(:create) expect(user_resource.updated_by_last_action?).to eq(expect_updated?) end context "and all properties are in the desired state" do let(:uid) { 1999 } let(:home) { "/home/bobo" } let(:manage_home) { true } # openssl passwd -1 "secretpassword" let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end let(:system) { false } let(:comment) { "hello this is dog" } let(:existing_uid) { uid } let(:existing_home) { home } let(:existing_manage_home) { manage_home } let(:existing_password) { password } let(:existing_system) { false } let(:existing_comment) { comment } let(:expect_updated?) { false } it "does not update the user" do expect(user_resource).not_to be_updated end end context "and the uid is updated" do let(:uid) { 1999 } let(:existing_uid) { 1998 } it "ensures the uid is set to the desired value" do expect(pw_entry.uid).to eq("1999") end end context "and the comment is updated" do let(:comment) { "hello this is dog" } let(:existing_comment) { "woof" } it "ensures the comment field is set to the desired value" do expect(pw_entry.gecos).to eq("hello this is dog") end end context "and home directory is updated" do let(:existing_home) { "/home/cheftestfoo" } let(:home) { "/home/cheftestbar" } it "ensures the home directory is set to the desired value" do expect(pw_entry.home).to eq("/home/cheftestbar") end context "and manage_home is enabled" do let(:existing_manage_home) { true } let(:manage_home) { true } it "moves the home directory to the new location" do expect(File).not_to exist("/home/cheftestfoo") expect(File).to exist("/home/cheftestbar") end end context "and manage_home wasn't enabled but is now" do let(:existing_manage_home) { false } let(:manage_home) { true } if %w{rhel fedora}.include?(OHAI_SYSTEM["platform_family"]) # Inconsistent behavior. See: CHEF-2205 it "created the home dir b/c of CHEF-2205 so it still exists" do # This behavior seems contrary to expectation and non-convergent. expect(File).not_to exist("/home/cheftestfoo") expect(File).to exist("/home/cheftestbar") end elsif ohai[:platform] == "aix" it "creates the home dir in the desired location" do expect(File).not_to exist("/home/cheftestfoo") expect(File).to exist("/home/cheftestbar") end else it "does not create the home dir in the desired location (XXX)" do # This behavior seems contrary to expectation and non-convergent. expect(File).not_to exist("/home/cheftestfoo") expect(File).not_to exist("/home/cheftestbar") end end end context "and manage_home was enabled but is not now" do let(:existing_manage_home) { true } let(:manage_home) { false } it "leaves the old home directory around (XXX)" do # Would it be better to remove the old home? expect(File).to exist("/home/cheftestfoo") expect(File).not_to exist("/home/cheftestbar") end end end context "and a password is added" do # openssl passwd -1 "secretpassword" let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end it "ensures the password is set" do password_should_be_set expect(etc_shadow).to include(expected_shadow) end end context "and the password is updated" do # openssl passwd -1 "OLDpassword" let(:existing_password) do case ohai[:platform] when "aix" "jkzG6MvUxjk2g" else "$1$1dVmwm4z$CftsFn8eBDjDRUytYKkXB." end end # openssl passwd -1 "secretpassword" let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end it "ensures the password is set to the desired value" do password_should_be_set expect(etc_shadow).to include(expected_shadow) end end context "and the user is changed from not-system to system" do let(:existing_system) { false } let(:system) { true } let(:expect_updated?) { false } it "does not modify the user at all" do end end context "and the user is changed from system to not-system" do let(:existing_system) { true } let(:system) { false } let(:expect_updated?) { false } it "does not modify the user at all" do end end end # when the user already exists end # action :create shared_context "user exists for lock/unlock" do let(:user_locked_context?) { false } def shadow_entry etc_shadow.lines.select {|l| l.include?(username) }.first end def shadow_password shadow_entry.split(':')[1] end def aix_user_lock_status lock_info = shell_out!("lsuser -a account_locked #{username}") status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1] end def user_account_should_be_locked case ohai[:platform] when "aix" expect(aix_user_lock_status).to eq("true") else expect(shadow_password).to include("!") end end def user_account_should_be_unlocked case ohai[:platform] when "aix" expect(aix_user_lock_status).to eq("false") else expect(shadow_password).not_to include("!") end end def lock_user_account case ohai[:platform] when "aix" shell_out!("chuser account_locked=true #{username}") else shell_out!("usermod -L #{username}") end end before do # create user and setup locked/unlocked state user_resource.dup.run_action(:create) if user_locked_context? lock_user_account user_account_should_be_locked elsif password user_account_should_be_unlocked end end end describe "action :lock" do context "when the user does not exist" do it "raises a sensible error" do expect { user_resource.run_action(:lock) }.to raise_error(Chef::Exceptions::User) end end context "when the user exists" do include_context "user exists for lock/unlock" before do user_resource.run_action(:lock) end context "and the user is not locked" do # user will be locked if it has no password let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end it "locks the user's password" do user_account_should_be_locked end end context "and the user is locked" do # user will be locked if it has no password let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end let(:user_locked_context?) { true } it "does not update the user" do expect(user_resource).not_to be_updated_by_last_action end end end end # action :lock describe "action :unlock" do context "when the user does not exist" do it "raises a sensible error" do expect { user_resource.run_action(:unlock) }.to raise_error(Chef::Exceptions::User) end end context "when the user exists" do include_context "user exists for lock/unlock" before do begin user_resource.run_action(:unlock) @error = nil rescue Exception => e @error = e end end context "and has no password" do # TODO: platform_family should be setup in spec_helper w/ tags if %w[suse opensuse].include?(OHAI_SYSTEM["platform_family"]) # suse gets this right: it "errors out trying to unlock the user" do expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed) expect(@error.message).to include("Cannot unlock the password") end else # borked on all other platforms: it "is marked as updated but doesn't modify the user (XXX)" do # This should be an error instead; note that usermod still exits 0 # (which is probably why this case silently fails): # # DEBUG: ---- Begin output of usermod -U chef-functional-test ---- # DEBUG: STDOUT: # DEBUG: STDERR: usermod: unlocking the user's password would result in a passwordless account. # You should set a password with usermod -p to unlock this user's password. # DEBUG: ---- End output of usermod -U chef-functional-test ---- # DEBUG: Ran usermod -U chef-functional-test returned 0 expect(@error).to be_nil if ohai[:platform] == "aix" expect(pw_entry.passwd).to eq('*') user_account_should_be_unlocked else expect(pw_entry.passwd).to eq('x') expect(shadow_password).to include("!") end end end end context "and has a password" do let(:password) do case ohai[:platform] when "aix" "eL5qfEVznSNss" else "$1$RRa/wMM/$XltKfoX5ffnexVF4dHZZf/" end end context "and the user is not locked" do it "does not update the user" do expect(user_resource).not_to be_updated_by_last_action end end context "and the user is locked" do let(:user_locked_context?) { true } it "unlocks the user's password" do user_account_should_be_unlocked end end end end end # action :unlock end chef-12.3.0/spec/functional/resource/user/dscl_spec.rb0000644000004100000410000001243112520074675022730 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/mixin/shell_out' metadata = { :mac_osx_only => true, :requires_root => true, :not_supported_on_mac_osx_106 => true, } describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metadata do include Chef::Mixin::ShellOut def clean_user begin shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'") rescue Mixlib::ShellOut::ShellCommandFailed # Raised when the user is already cleaned end end def user_should_exist expect(shell_out("/usr/bin/dscl . -ls /Users").stdout).to include username end def check_password(pass) # In order to test the password we use dscl passwd command since # that's the only command that gets the user password from CLI. expect(shell_out("dscl . -passwd /Users/greatchef #{pass} new_password").exitstatus).to eq(0) # Now reset the password back expect(shell_out("dscl . -passwd /Users/greatchef new_password #{pass}").exitstatus).to eq(0) end let(:node) do n = Chef::Node.new n.consume_external_attrs(OHAI_SYSTEM.data.dup, {}) n end let(:events) do Chef::EventDispatch::Dispatcher.new end let(:run_context) do Chef::RunContext.new(node, {}, events) end let(:username) do "greatchef" end let(:uid) { nil } let(:gid) { 20 } let(:home) { nil } let(:manage_home) { false } let(:password) { "XXXYYYZZZ" } let(:comment) { "Great Chef" } let(:shell) { "/bin/bash" } let(:salt) { nil } let(:iterations) { nil } let(:user_resource) do r = Chef::Resource::User.new("TEST USER RESOURCE", run_context) r.username(username) r.uid(uid) r.gid(gid) r.home(home) r.shell(shell) r.comment(comment) r.manage_home(manage_home) r.password(password) r.salt(salt) r.iterations(iterations) r end before do clean_user end after(:each) do clean_user end describe "action :create" do it "should create the user" do user_resource.run_action(:create) user_should_exist check_password(password) end end describe "when user exists" do before do existing_resource = user_resource.dup existing_resource.run_action(:create) user_should_exist end describe "when password is updated" do it "should update the password of the user" do user_resource.password("mykitchen") user_resource.run_action(:create) check_password("mykitchen") end end end describe "when password is being set via shadow hash" do let(:password) { if node[:platform_version].start_with?("10.7.") # On Mac 10.7 we only need to set the password "c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\ d0f5e65c61558243e50b1a490088bd4824e3b35562d383ca02260398\ ef1979b302212ec1c5383d1d05fc8d843" else "c734b6e4787c3727bb35e29fdd92b97c\ 1de12df509577a045728255ec7c6c5f5\ c18efa05ed02b682ffa7ebc05119900e\ b1d4880833aa7a190afc13e2bf0936b8\ 20123e8c98f0f9bcac2a629d9163caac\ 9464a8c234f3919082400b4f939bb77b\ c5adbbac718b7eb99463a7b679571e0f\ 1c9fef2ef08d0b9e9c2bcf644eed2ffc" end } let(:iterations) { 25000 } let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" } it "action :create should create the user" do user_resource.run_action(:create) user_should_exist check_password("soawesome") end describe "when user exists" do before do existing_resource = user_resource.dup existing_resource.run_action(:create) user_should_exist end describe "when password is updated" do it "should update the password of the user" do user_resource.password("mykitchen") user_resource.run_action(:create) check_password("mykitchen") end end end end describe "when a user is member of some groups" do let(:groups) { ["staff", "operator"] } before do existing_resource = user_resource.dup existing_resource.run_action(:create) groups.each do |group| shell_out!("/usr/bin/dscl . -append '/Groups/#{group}' GroupMembership #{username}") end end after do groups.each do |group| # Do not raise an error when user is correctly removed shell_out("/usr/bin/dscl . -delete '/Groups/#{group}' GroupMembership #{username}") end end it ":remove action removes the user from the groups and deletes the user"do user_resource.run_action(:remove) groups.each do |group| # Do not raise an error when group is empty expect(shell_out("dscl . read /Groups/staff GroupMembership").stdout).not_to include(group) end end end end chef-12.3.0/spec/functional/resource/remote_file_spec.rb0000644000004100000410000001502212520074675023316 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' require 'tiny_server' require 'support/shared/functional/http' describe Chef::Resource::RemoteFile do include ChefHTTPShared let(:file_cache_path) { Dir.mktmpdir } before(:each) do @old_file_cache = Chef::Config[:file_cache_path] Chef::Config[:file_cache_path] = file_cache_path end after(:each) do Chef::Config[:file_cache_path] = @old_file_cache FileUtils.rm_rf(file_cache_path) end include_context Chef::Resource::File let(:file_base) { "remote_file_spec" } def create_resource node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) resource = Chef::Resource::RemoteFile.new(path, run_context) resource.source(source) resource end let(:resource) do create_resource end let(:default_mode) { (0666 & ~File.umask).to_s(8) } context "when fetching files over HTTP" do before(:all) do start_tiny_server end after(:all) do stop_tiny_server end describe "when redownload isn't necessary" do let(:source) { 'http://localhost:9000/seattle_capo.png' } before do @api.get("/seattle_capo.png", 304, "", { 'Etag' => 'abcdef' } ) end it "does not fetch the file" do resource.run_action(:create) end end context "when using normal encoding" do let(:source) { 'http://localhost:9000/nyan_cat.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "a file resource" it_behaves_like "a securable resource with reporting" end context "when using gzip encoding" do let(:source) { 'http://localhost:9000/nyan_cat.png.gz' } let(:expected_content) { binread(nyan_compressed_filename) } it_behaves_like "a file resource" it_behaves_like "a securable resource with reporting" end end context "when fetching files over HTTPS" do before(:all) do cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA)) cert = OpenSSL::X509::Certificate.new(cert_text) key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA)) key = OpenSSL::PKey::RSA.new(key_text) server_opts = { :SSLEnable => true, :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE, :SSLCertificate => cert, :SSLPrivateKey => key } start_tiny_server(server_opts) end after(:all) do stop_tiny_server end let(:source) { 'https://localhost:9000/nyan_cat.png' } let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "a file resource" end context "when dealing with content length checking" do before(:all) do start_tiny_server end after(:all) do stop_tiny_server end context "when downloading compressed data" do let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_content_length_compressed.png' } before do expect(File).not_to exist(path) resource.run_action(:create) end it "should create the file" do expect(File).to exist(path) end it "should mark the resource as updated" do expect(resource).to be_updated_by_last_action end it "has the correct content" do expect(binread(path)).to eq(expected_content) end end context "when downloding uncompressed data" do let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_content_length.png' } before do expect(File).not_to exist(path) resource.run_action(:create) end it "should create the file" do expect(File).to exist(path) end it "should mark the resource as updated" do expect(resource).to be_updated_by_last_action end it "has the correct content" do expect(binread(path)).to eq(expected_content) end end context "when downloading truncated compressed data" do let(:source) { 'http://localhost:9000/nyan_cat_truncated_compressed.png' } before do expect(File).not_to exist(path) end it "should raise ContentLengthMismatch" do expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) #File.should_not exist(path) # XXX: CHEF-5081 end end context "when downloding truncated uncompressed data" do let(:source) { 'http://localhost:9000/nyan_cat_truncated.png' } before do expect(File).not_to exist(path) end it "should raise ContentLengthMismatch" do expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) #File.should_not exist(path) # XXX: CHEF-5081 end end context "when downloding data with transfer-encoding set" do let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_transfer_encoding.png' } before do expect(File).not_to exist(path) resource.run_action(:create) end it "should create the file" do expect(File).to exist(path) end it "should mark the resource as updated" do expect(resource).to be_updated_by_last_action end it "has the correct content" do expect(binread(path)).to eq(expected_content) end end describe "when the download of the source raises an exception" do let(:source) { 'http://localhost:0000/seattle_capo.png' } before do expect(File).not_to exist(path) end it "should not create the file" do expect{ resource.run_action(:create) }.to raise_error expect(File).not_to exist(path) end end end end chef-12.3.0/spec/functional/resource/reboot_spec.rb0000644000004100000410000000610312520074675022316 0ustar www-datawww-data# # Author:: Chris Doherty ) # Copyright:: Copyright (c) 2014 Chef, 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::Reboot do let(:expected) do { :delay_mins => 5, :requested_by => "reboot resource functional test", :reason => "reboot resource spec test" } end def create_resource node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) resource = Chef::Resource::Reboot.new(expected[:requested_by], run_context) resource.delay_mins(expected[:delay_mins]) resource.reason(expected[:reason]) resource end let(:resource) do create_resource end shared_context 'testing run context modification' do def test_reboot_action(resource) reboot_info = resource.run_context.reboot_info expect(reboot_info.keys.sort).to eq([:delay_mins, :reason, :requested_by, :timestamp]) expect(reboot_info[:delay_mins]).to eq(expected[:delay_mins]) expect(reboot_info[:reason]).to eq(expected[:reason]) expect(reboot_info[:requested_by]).to eq(expected[:requested_by]) expect(resource.run_context.reboot_requested?).to be_truthy end end # the currently defined behavior for multiple calls to this resource is "last one wins." describe 'the request_reboot_on_successful_run action' do include_context 'testing run context modification' before do resource.run_action(:request_reboot) end after do resource.run_context.cancel_reboot end it 'should have modified the run context correctly' do test_reboot_action(resource) end end describe 'the reboot_interrupt_run action' do include_context 'testing run context modification' after do resource.run_context.cancel_reboot end it 'should have modified the run context correctly' do # this doesn't actually test the flow of Chef::Client#do_run, unfortunately. expect { resource.run_action(:reboot_now) }.to throw_symbol(:end_client_run_early) test_reboot_action(resource) end end describe "the cancel action" do before do resource.run_context.request_reboot(expected) resource.run_action(:cancel) end it 'should have cleared the reboot request' do # arguably we shouldn't be querying RunContext's internal data directly. expect(resource.run_context.reboot_info).to eq({}) expect(resource.run_context.reboot_requested?).to be_falsey end end end chef-12.3.0/spec/functional/resource/rpm_spec.rb0000644000004100000410000000775412520074675021637 0ustar www-datawww-data# # Author:: Prabhu Das () # 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/base' require 'chef/mixin/shell_out' # run this test only for following platforms. exclude_test = !['aix', 'centos', 'redhat', 'suse'].include?(ohai[:platform]) describe Chef::Resource::RpmPackage, :requires_root, :external => exclude_test do include Chef::Mixin::ShellOut let(:new_resource) do new_resource = Chef::Resource::RpmPackage.new(@pkg_name, run_context) new_resource.source @pkg_path new_resource end def rpm_pkg_should_be_installed(resource) case ohai[:platform] # Due to dependency issues , different rpm pkgs are used in different platforms. # dummy rpm package works in aix, without any dependency issues. when "aix" expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(0) # mytest rpm package works in centos, redhat and in suse without any dependency issues. when "centos", "redhat", "suse" expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(0) ::File.exists?("/opt/mytest/mytest.sh") # The mytest rpm package contains the mytest.sh file end end def rpm_pkg_should_not_be_installed(resource) case ohai[:platform] when "aix" expect(shell_out("rpm -qa | grep dummy").exitstatus).to eq(1) when "centos", "redhat", "suse" expect(shell_out("rpm -qa | grep mytest").exitstatus).to eq(1) !::File.exists?("/opt/mytest/mytest.sh") end end before(:all) do case ohai[:platform] # Due to dependency issues , different rpm pkgs are used in different platforms. when "aix" @pkg_name = "dummy" @pkg_version = "1-0" @pkg_path = "/tmp/dummy-1-0.aix6.1.noarch.rpm" FileUtils.cp(File.join(CHEF_SPEC_ASSETS, 'dummy-1-0.aix6.1.noarch.rpm') , @pkg_path) when "centos", "redhat", "suse" @pkg_name = "mytest" @pkg_version = "1.0-1" @pkg_path = "/tmp/mytest-1.0-1.noarch.rpm" FileUtils.cp(File.join(CHEF_SPEC_ASSETS, 'mytest-1.0-1.noarch.rpm') , @pkg_path) end end after(:all) do FileUtils.rm @pkg_path end context "package install action" do it "should create a package" do new_resource.run_action(:install) rpm_pkg_should_be_installed(new_resource) end after(:each) do shell_out("rpm -qa | grep #{@pkg_name}-#{@pkg_version} | xargs rpm -e") end end context "package remove action" do before(:each) do shell_out("rpm -i #{@pkg_path}") end it "should remove an existing package" do new_resource.run_action(:remove) rpm_pkg_should_not_be_installed(new_resource) end end context "package upgrade action" do before(:each) do shell_out("rpm -i #{@pkg_path}") if ohai[:platform] == 'aix' @pkg_version = "2-0" @pkg_path = "/tmp/dummy-2-0.aix6.1.noarch.rpm" FileUtils.cp(File.join(CHEF_SPEC_ASSETS, 'dummy-2-0.aix6.1.noarch.rpm') , @pkg_path) else @pkg_version = "2.0-1" @pkg_path = "/tmp/mytest-2.0-1.noarch.rpm" FileUtils.cp(File.join(CHEF_SPEC_ASSETS, 'mytest-2.0-1.noarch.rpm') , @pkg_path) end end it "should upgrade a package" do new_resource.run_action(:install) rpm_pkg_should_be_installed(new_resource) end after(:each) do shell_out("rpm -qa | grep #{@pkg_name}-#{@pkg_version} | xargs rpm -e") FileUtils.rm @pkg_path end end end chef-12.3.0/spec/functional/resource/dsc_resource_spec.rb0000644000004100000410000000577512520074675023522 0ustar www-datawww-data# # Author:: Jay Mundrawala () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' describe Chef::Resource::DscResource, :windows_powershell_dsc_only do before(:all) do @ohai = Ohai::System.new @ohai.all_plugins(['platform', 'os', 'languages/powershell']) end let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new } let(:node) { Chef::Node.new.tap do |n| n.consume_external_attrs(@ohai.data, {}) end } let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) } let(:new_resource) { Chef::Resource::DscResource.new("dsc_resource_test", run_context) } context 'when Powershell does not support Invoke-DscResource' context 'when Powershell supports Invoke-DscResource' do before do if !Chef::Platform.supports_dsc_invoke_resource?(node) skip 'Requires Powershell >= 5.0.10018.0' end end context 'with an invalid dsc resource' do it 'raises an exception if the resource is not found' do new_resource.resource 'thisdoesnotexist' expect { new_resource.run_action(:run) }.to raise_error( Chef::Exceptions::ResourceNotFound) end end context 'with a valid dsc resource' do let(:tmp_file_name) { Dir::Tmpname.create('tmpfile') {} } let(:test_text) { "'\"!@#$%^&*)(}{][\u2713~n"} before do new_resource.resource :File new_resource.property :Contents, test_text new_resource.property :DestinationPath, tmp_file_name end after do File.delete(tmp_file_name) if File.exists? tmp_file_name end it 'converges the resource if it is not converged' do new_resource.run_action(:run) contents = File.open(tmp_file_name, 'rb:bom|UTF-16LE') do |f| f.read.encode('UTF-8') end expect(contents).to eq(test_text) expect(new_resource).to be_updated end it 'does not converge the resource if it is already converged' do new_resource.run_action(:run) expect(new_resource).to be_updated reresource = Chef::Resource::DscResource.new("dsc_resource_retest", run_context) reresource.resource :File reresource.property :Contents, test_text reresource.property :DestinationPath, tmp_file_name reresource.run_action(:run) expect(reresource).not_to be_updated end end end end chef-12.3.0/spec/functional/resource/file_spec.rb0000644000004100000410000000721612520074675021751 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' require 'tmpdir' describe Chef::Resource::File do include_context Chef::Resource::File let(:file_base) { "file_spec" } let(:expected_content) { "Don't fear the ruby." } def create_resource(opts={}) events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new run_context = Chef::RunContext.new(node, {}, events) use_path = if opts[:use_relative_path] Dir.chdir(Dir.tmpdir) File.basename(path) else path end Chef::Resource::File.new(use_path, run_context) end let(:resource) do r = create_resource r.content(expected_content) r end let(:resource_without_content) do create_resource end let(:resource_with_relative_path) do create_resource(:use_relative_path => true) end let(:unmanaged_content) do "This is file content that is not managed by chef" end let(:current_resource) do provider = resource.provider_for_action(resource.action) provider.load_current_resource provider.current_resource end let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a file resource" it_behaves_like "a securable resource with reporting" describe "when running action :create without content" do before do resource_without_content.run_action(:create) end context "and the target file does not exist" do it "creates the file" do expect(File).to exist(path) end it "is marked updated by last action" do expect(resource_without_content).to be_updated_by_last_action end end end # github issue 1842. describe "when running action :create on a relative path" do before do resource_with_relative_path.run_action(:create) end context "and the file exists" do it "should run without an exception" do resource_with_relative_path.run_action(:create) end end end describe "when running action :touch" do context "and the target file does not exist" do before do resource.run_action(:touch) end it "it creates the file" do expect(File).to exist(path) end it "is marked updated by last action" do expect(resource).to be_updated_by_last_action end end context "and the target file exists and has the correct content" do before(:each) do File.open(path, "w") { |f| f.print expected_content } @expected_checksum = sha256_checksum(path) now = Time.now.to_i File.utime(now - 9000, now - 9000, path) @expected_mtime = File.stat(path).mtime resource.run_action(:touch) end it "updates the mtime of the file" do expect(File.stat(path).mtime).to be > @expected_mtime end it "does not change the content" do expect(sha256_checksum(path)).to eq(@expected_checksum) end it "is marked as updated by last action" do expect(resource).to be_updated_by_last_action end end end end chef-12.3.0/spec/functional/resource/aixinit_service_spec.rb0000755000004100000410000001422512520074675024220 0ustar www-datawww-data# encoding: UTF-8 # # Author:: Kaustubh Deorukhkar () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'functional/resource/base' require 'chef/mixin/shell_out' require 'fileutils' describe Chef::Resource::Service, :requires_root, :aix_only do include Chef::Mixin::ShellOut # Platform specific validation routines. def service_should_be_started(file_name) # The existence of this file indicates that the service was started. expect(File.exists?("/tmp/#{file_name}")).to be_truthy end def service_should_be_stopped(file_name) expect(File.exists?("/tmp/#{file_name}")).to be_falsey end def valide_symlinks(expected_output, run_level = nil, status = nil, priority = nil) directory = [] if priority.is_a?Hash priority.each do |level,o| directory << "/etc/rc.d/rc#{level}.d/#{(o[0] == :start ? 'S' : 'K')}#{o[1]}#{new_resource.service_name}" end directory else directory << "/etc/rc.d/rc#{run_level}.d/#{status}#{priority}#{new_resource.service_name}" end expect(Dir.glob(directory)).to eq(expected_output) File.delete(*directory) end def delete_test_files files = Dir.glob("/tmp/chefinit[a-z_]*.txt") File.delete(*files) end # Actual tests let(:new_resource) do new_resource = Chef::Resource::Service.new("chefinittest", run_context) new_resource.provider Chef::Provider::Service::AixInit new_resource.supports({:status => true, :restart => true, :reload => true}) new_resource end let(:provider) do provider = new_resource.provider_for_action(new_resource.action) provider end before(:all) do File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest") FileUtils.cp("#{File.join(File.dirname(__FILE__), "/../assets/chefinittest")}", "/etc/rc.d/init.d/chefinittest") end after(:all) do File.delete("/etc/rc.d/init.d/chefinittest") if File.exists?("/etc/rc.d/init.d/chefinittest") end before(:each) do delete_test_files end after(:each) do delete_test_files end describe "start service" do it "should start the service" do new_resource.run_action(:start) service_should_be_started("chefinittest.txt") end end describe "stop service" do before do new_resource.run_action(:start) end it "should stop the service" do new_resource.run_action(:stop) service_should_be_stopped("chefinittest.txt") end end describe "restart service" do before do new_resource.run_action(:start) end it "should restart the service" do new_resource.run_action(:restart) service_should_be_started("chefinittest_restart.txt") end end describe "reload service" do before do new_resource.run_action(:start) end it "should reload the service" do new_resource.run_action(:reload) service_should_be_started("chefinittest_reload.txt") end end describe "enable service" do context "when the service doesn't set a priority" do it "creates symlink with status S" do new_resource.run_action(:enable) valide_symlinks(["/etc/rc.d/rc2.d/Schefinittest"],2,'S') end end context "when the service sets a simple priority (integer)" do before do new_resource.priority(75) end it "creates a symlink with status S and a priority" do new_resource.run_action(:enable) valide_symlinks(["/etc/rc.d/rc2.d/S75chefinittest"], 2,'S',75) end end context "when the service sets complex priorities (hash)" do before do priority = {2 => [:start, 20], 3 => [:stop, 10]} new_resource.priority(priority) end it "create symlink with status start (S) or stop (K) and a priority " do new_resource.run_action(:enable) valide_symlinks(["/etc/rc.d/rc2.d/S20chefinittest", "/etc/rc.d/rc3.d/K10chefinittest"], 2,'S',new_resource.priority) end end end describe "disable_service" do context "when the service doesn't set a priority" do before do File.symlink("/etc/rc.d/init.d/chefinittest", "/etc/rc.d/rc2.d/Schefinittest") end after do File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest") end it "creates symlink with status K" do new_resource.run_action(:disable) valide_symlinks(["/etc/rc.d/rc2.d/Kchefinittest"], 2,'K') end end context "when the service sets a simple priority (integer)" do before do new_resource.priority(75) File.symlink("/etc/rc.d/init.d/chefinittest", "/etc/rc.d/rc2.d/Schefinittest") end after do File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest") end it "creates a symlink with status K and a priority" do new_resource.run_action(:disable) valide_symlinks(["/etc/rc.d/rc2.d/K25chefinittest"], 2,'K',25) end end context "when the service sets complex priorities (hash)" do before do @priority = {2 => [:stop, 20], 3 => [:start, 10]} new_resource.priority(@priority) File.symlink("/etc/rc.d/init.d/chefinittest", "/etc/rc.d/rc2.d/Schefinittest") end after do File.delete("/etc/rc.d/rc2.d/Schefinittest") if File.exists?("/etc/rc.d/rc2.d/chefinittest") end it "create symlink with status stop (K) and a priority " do new_resource.run_action(:disable) valide_symlinks(["/etc/rc.d/rc2.d/K80chefinittest"], 2,'K',80) end end end endchef-12.3.0/spec/functional/resource/registry_spec.rb0000644000004100000410000006054712520074675022710 0ustar www-datawww-data# # Author:: Prajakta Purohit () # Author:: Lamont Granquist () # 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 "chef/win32/registry" require "chef/resource_reporter" require "spec_helper" describe Chef::Resource::RegistryKey, :unix_only do before(:all) do events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins node.consume_external_attrs(ohai.data,{}) run_context = Chef::RunContext.new(node, {}, events) @resource = Chef::Resource::RegistryKey.new("HKCU\\Software", run_context) end context "when load_current_resource is run on a non-windows node" do it "throws an exception because you don't have a windows registry (derp)" do @resource.key("HKCU\\Software\\Opscode") @resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) expect{@resource.run_action(:create)}.to raise_error(Chef::Exceptions::Win32NotWindows) end end end describe Chef::Resource::RegistryKey, :windows_only, :broken => true do # parent and key must be single keys, not paths let(:parent) { 'Opscode' } let(:child) { 'Whatever' } let(:key_parent) { "SOFTWARE\\" + parent } let(:key_child) { "SOFTWARE\\" + parent + "\\" + child } # must be under HKLM\SOFTWARE for WOW64 redirection to work let(:reg_parent) { "HKLM\\" + key_parent } let(:reg_child) { "HKLM\\" + key_child } let(:hive_class) { ::Win32::Registry::HKEY_LOCAL_MACHINE } let(:resource_name) { "This is the name of my Resource" } def clean_registry if windows64? # clean 64-bit space on WOW64 @registry.architecture = :x86_64 @registry.delete_key(reg_parent, true) @registry.architecture = :machine end # clean 32-bit space on WOW64 @registry.architecture = :i386 @registry.delete_key(reg_parent, true) @registry.architecture = :machine end def reset_registry clean_registry hive_class.create(key_parent, Win32::Registry::KEY_WRITE | 0x0100) hive_class.create(key_parent, Win32::Registry::KEY_WRITE | 0x0200) end def create_deletable_keys # create them both 32-bit and 64-bit [ 0x0100, 0x0200 ].each do |flag| hive_class.create(key_parent + '\Opscode', Win32::Registry::KEY_WRITE | flag) hive_class.open(key_parent + '\Opscode', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg| reg["Color", Win32::Registry::REG_SZ] = "Orange" reg.write("Opscode", Win32::Registry::REG_MULTI_SZ, ["Seattle", "Washington"]) reg["AKA", Win32::Registry::REG_SZ] = "OC" end hive_class.create(key_parent + '\ReportKey', Win32::Registry::KEY_WRITE | flag) hive_class.open(key_parent + '\ReportKey', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg| reg["ReportVal4", Win32::Registry::REG_SZ] = "report4" reg["ReportVal5", Win32::Registry::REG_SZ] = "report5" end hive_class.create(key_parent + '\OpscodeWhyRun', Win32::Registry::KEY_WRITE | flag) hive_class.open(key_parent + '\OpscodeWhyRun', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg| reg["BriskWalk", Win32::Registry::REG_SZ] = "is good for health" end end end before(:all) do @events = Chef::EventDispatch::Dispatcher.new @node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins @node.consume_external_attrs(ohai.data,{}) @run_context = Chef::RunContext.new(@node, {}, @events) @new_resource = Chef::Resource::RegistryKey.new(resource_name, @run_context) @registry = Chef::Win32::Registry.new(@run_context) reset_registry end #Reporting setup before do @node.name("windowsbox") @rest_client = double("Chef::REST (mock)") allow(@rest_client).to receive(:create_url).and_return("reports/nodes/windowsbox/runs/#{@run_id}"); allow(@rest_client).to receive(:raw_http_request).and_return({"result"=>"ok"}); allow(@rest_client).to receive(:post_rest).and_return({"uri"=>"https://example.com/reports/nodes/windowsbox/runs/#{@run_id}"}); @resource_reporter = Chef::ResourceReporter.new(@rest_client) @events.register(@resource_reporter) @run_status = Chef::RunStatus.new(@node, @events) @resource_reporter.run_started(@run_status) @run_id = @resource_reporter.run_id @new_resource.cookbook_name = "monkey" @cookbook_version = double("Cookbook::Version", :version => "1.2.3") allow(@new_resource).to receive(:cookbook_version).and_return(@cookbook_version) end after (:all) do clean_registry end context "when action is create" do before (:all) do reset_registry end it "creates registry key, value if the key is missing" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) @new_resource.run_action(:create) expect(@registry.key_exists?(reg_child)).to eq(true) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) end it "does not create the key if it already exists with same value, type and data" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) @new_resource.run_action(:create) expect(@registry.key_exists?(reg_child)).to eq(true) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) end it "creates a value if it does not exist" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Mango", :type=>:string, :data=>"Yellow"}]) @new_resource.run_action(:create) expect(@registry.data_exists?(reg_child, {:name=>"Mango", :type=>:string, :data=>"Yellow"})).to eq(true) end it "modifies the data if the key and value exist and type matches" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Not just Orange - OpscodeOrange!"}]) @new_resource.run_action(:create) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Not just Orange - OpscodeOrange!"})).to eq(true) end it "modifys the type if the key and value exist and the type does not match" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:multi_string, :data=>["Not just Orange - OpscodeOrange!"]}]) @new_resource.run_action(:create) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:multi_string, :data=>["Not just Orange - OpscodeOrange!"]})).to eq(true) end it "creates subkey if parent exists" do @new_resource.key(reg_child + '\OpscodeTest') @new_resource.values([{:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}]) @new_resource.recursive(false) @new_resource.run_action(:create) expect(@registry.key_exists?(reg_child + '\OpscodeTest')).to eq(true) expect(@registry.value_exists?(reg_child + '\OpscodeTest', {:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]})).to eq(true) end it "gives error if action create and parent does not exist and recursive is set to false" do @new_resource.key(reg_child + '\Missing1\Missing2') @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}]) @new_resource.recursive(false) expect{@new_resource.run_action(:create)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) end it "creates missing keys if action create and parent does not exist and recursive is set to true" do @new_resource.key(reg_child + '\Missing1\Missing2') @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}]) @new_resource.recursive(true) @new_resource.run_action(:create) expect(@registry.key_exists?(reg_child + '\Missing1\Missing2')).to eq(true) expect(@registry.value_exists?(reg_child + '\Missing1\Missing2', {:name=>"OC", :type=>:string, :data=>"MissingData"})).to eq(true) end it "creates key with multiple value as specified" do @new_resource.key(reg_child) @new_resource.values([{:name=>"one", :type=>:string, :data=>"1"},{:name=>"two", :type=>:string, :data=>"2"},{:name=>"three", :type=>:string, :data=>"3"}]) @new_resource.recursive(true) @new_resource.run_action(:create) @new_resource.values.each do |value| expect(@registry.value_exists?(reg_child, value)).to eq(true) end end context "when running on 64-bit server", :windows64_only do before(:all) do reset_registry end after(:all) do @new_resource.architecture(:machine) @registry.architecture = :machine end it "creates a key in a 32-bit registry that is not viewable in 64-bit" do @new_resource.key(reg_child + '\Atraxi' ) @new_resource.values([{:name=>"OC", :type=>:string, :data=>"Data"}]) @new_resource.recursive(true) @new_resource.architecture(:i386) @new_resource.run_action(:create) @registry.architecture = :i386 expect(@registry.data_exists?(reg_child + '\Atraxi', {:name=>"OC", :type=>:string, :data=>"Data"})).to eq(true) @registry.architecture = :x86_64 expect(@registry.key_exists?(reg_child + '\Atraxi')).to eq(false) end end it "prepares the reporting data for action :create" do @new_resource.key(reg_child + '\Ood') @new_resource.values([{:name=>"ReportingVal1", :type=>:string, :data=>"report1"},{:name=>"ReportingVal2", :type=>:string, :data=>"report2"}]) @new_resource.recursive(true) @new_resource.run_action(:create) @report = @resource_reporter.prepare_run_data expect(@report["action"]).to eq("end") expect(@report["resources"][0]["type"]).to eq("registry_key") expect(@report["resources"][0]["name"]).to eq(resource_name) expect(@report["resources"][0]["id"]).to eq(reg_child + '\Ood') expect(@report["resources"][0]["after"][:values]).to eq([{:name=>"ReportingVal1", :type=>:string, :data=>"report1"}, {:name=>"ReportingVal2", :type=>:string, :data=>"report2"}]) expect(@report["resources"][0]["before"][:values]).to eq([]) expect(@report["resources"][0]["result"]).to eq("create") expect(@report["status"]).to eq("success") expect(@report["total_res_count"]).to eq("1") end context "while running in whyrun mode" do before (:each) do Chef::Config[:why_run] = true end it "does not throw an exception if the keys do not exist but recursive is set to false" do @new_resource.key(reg_child + '\Slitheen\Raxicoricofallapatorius') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:create) # should not raise_error expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false) expect(@registry.key_exists?(reg_child + '\Slitheen\Raxicoricofallapatorius')).to eq(false) end it "does not create key if the action is create" do @new_resource.key(reg_child + '\Slitheen') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:create) expect(@registry.key_exists?(reg_child + '\Slitheen')).to eq(false) end end end context "when action is create_if_missing" do before (:all) do reset_registry end it "creates registry key, value if the key is missing" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) @new_resource.run_action(:create_if_missing) expect(@registry.key_exists?(reg_parent)).to eq(true) expect(@registry.key_exists?(reg_child)).to eq(true) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) end it "does not create the key if it already exists with same value, type and data" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) @new_resource.run_action(:create_if_missing) expect(@registry.key_exists?(reg_child)).to eq(true) expect(@registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) end it "creates a value if it does not exist" do @new_resource.key(reg_child) @new_resource.values([{:name=>"Mango", :type=>:string, :data=>"Yellow"}]) @new_resource.run_action(:create_if_missing) expect(@registry.data_exists?(reg_child, {:name=>"Mango", :type=>:string, :data=>"Yellow"})).to eq(true) end it "creates subkey if parent exists" do @new_resource.key(reg_child + '\Pyrovile') @new_resource.values([{:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}]) @new_resource.recursive(false) @new_resource.run_action(:create_if_missing) expect(@registry.key_exists?(reg_child + '\Pyrovile')).to eq(true) expect(@registry.value_exists?(reg_child + '\Pyrovile', {:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]})).to eq(true) end it "gives error if action create and parent does not exist and recursive is set to false" do @new_resource.key(reg_child + '\Sontaran\Sontar') @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}]) @new_resource.recursive(false) expect{@new_resource.run_action(:create_if_missing)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) end it "creates missing keys if action create and parent does not exist and recursive is set to true" do @new_resource.key(reg_child + '\Sontaran\Sontar') @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}]) @new_resource.recursive(true) @new_resource.run_action(:create_if_missing) expect(@registry.key_exists?(reg_child + '\Sontaran\Sontar')).to eq(true) expect(@registry.value_exists?(reg_child + '\Sontaran\Sontar', {:name=>"OC", :type=>:string, :data=>"MissingData"})).to eq(true) end it "creates key with multiple value as specified" do @new_resource.key(reg_child + '\Adipose') @new_resource.values([{:name=>"one", :type=>:string, :data=>"1"},{:name=>"two", :type=>:string, :data=>"2"},{:name=>"three", :type=>:string, :data=>"3"}]) @new_resource.recursive(true) @new_resource.run_action(:create_if_missing) @new_resource.values.each do |value| expect(@registry.value_exists?(reg_child + '\Adipose', value)).to eq(true) end end it "prepares the reporting data for :create_if_missing" do @new_resource.key(reg_child + '\Judoon') @new_resource.values([{:name=>"ReportingVal3", :type=>:string, :data=>"report3"}]) @new_resource.recursive(true) @new_resource.run_action(:create_if_missing) @report = @resource_reporter.prepare_run_data expect(@report["action"]).to eq("end") expect(@report["resources"][0]["type"]).to eq("registry_key") expect(@report["resources"][0]["name"]).to eq(resource_name) expect(@report["resources"][0]["id"]).to eq(reg_child + '\Judoon') expect(@report["resources"][0]["after"][:values]).to eq([{:name=>"ReportingVal3", :type=>:string, :data=>"report3"}]) expect(@report["resources"][0]["before"][:values]).to eq([]) expect(@report["resources"][0]["result"]).to eq("create_if_missing") expect(@report["status"]).to eq("success") expect(@report["total_res_count"]).to eq("1") end context "while running in whyrun mode" do before (:each) do Chef::Config[:why_run] = true end it "does not throw an exception if the keys do not exist but recursive is set to false" do @new_resource.key(reg_child + '\Zygons\Zygor') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:create_if_missing) # should not raise_error expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false) expect(@registry.key_exists?(reg_child + '\Zygons\Zygor')).to eq(false) end it "does nothing if the action is create_if_missing" do @new_resource.key(reg_child + '\Zygons') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:create_if_missing) expect(@registry.key_exists?(reg_child + '\Zygons')).to eq(false) end end end context "when the action is delete" do before(:all) do reset_registry create_deletable_keys end it "takes no action if the specified key path does not exist in the system" do expect(@registry.key_exists?(reg_parent + '\Osirian')).to eq(false) @new_resource.key(reg_parent+ '\Osirian') @new_resource.recursive(false) @new_resource.run_action(:delete) expect(@registry.key_exists?(reg_parent + '\Osirian')).to eq(false) end it "takes no action if the key exists but the value does not" do expect(@registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) @new_resource.key(reg_parent + '\Opscode') @new_resource.values([{:name=>"LooksLike", :type=>:multi_string, :data=>["SeattleGrey", "OCOrange"]}]) @new_resource.recursive(false) @new_resource.run_action(:delete) expect(@registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) end it "deletes only specified values under a key path" do @new_resource.key(reg_parent + '\Opscode') @new_resource.values([{:name=>"Opscode", :type=>:multi_string, :data=>["Seattle", "Washington"]}, {:name=>"AKA", :type=>:string, :data=>"OC"}]) @new_resource.recursive(false) @new_resource.run_action(:delete) expect(@registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(true) expect(@registry.value_exists?(reg_parent + '\Opscode', {:name=>"AKA", :type=>:string, :data=>"OC"})).to eq(false) expect(@registry.value_exists?(reg_parent + '\Opscode', {:name=>"Opscode", :type=>:multi_string, :data=>["Seattle", "Washington"]})).to eq(false) end it "it deletes the values with the same name irrespective of it type and data" do @new_resource.key(reg_parent + '\Opscode') @new_resource.values([{:name=>"Color", :type=>:multi_string, :data=>["Black", "Orange"]}]) @new_resource.recursive(false) @new_resource.run_action(:delete) expect(@registry.value_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"})).to eq(false) end it "prepares the reporting data for action :delete" do @new_resource.key(reg_parent + '\ReportKey') @new_resource.values([{:name=>"ReportVal4", :type=>:string, :data=>"report4"},{:name=>"ReportVal5", :type=>:string, :data=>"report5"}]) @new_resource.recursive(true) @new_resource.run_action(:delete) @report = @resource_reporter.prepare_run_data expect(@registry.value_exists?(reg_parent + '\ReportKey', [{:name=>"ReportVal4", :type=>:string, :data=>"report4"},{:name=>"ReportVal5", :type=>:string, :data=>"report5"}])).to eq(false) expect(@report["action"]).to eq("end") expect(@report["resources"].count).to eq(1) expect(@report["resources"][0]["type"]).to eq("registry_key") expect(@report["resources"][0]["name"]).to eq(resource_name) expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey') expect(@report["resources"][0]["before"][:values]).to eq([{:name=>"ReportVal4", :type=>:string, :data=>"report4"}, {:name=>"ReportVal5", :type=>:string, :data=>"report5"}]) #Not testing for after values to match since after -> new_resource values. expect(@report["resources"][0]["result"]).to eq("delete") expect(@report["status"]).to eq("success") expect(@report["total_res_count"]).to eq("1") end context "while running in whyrun mode" do before (:each) do Chef::Config[:why_run] = true end it "does nothing if the action is delete" do @new_resource.key(reg_parent + '\OpscodeWhyRun') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:delete) expect(@registry.key_exists?(reg_parent + '\OpscodeWhyRun')).to eq(true) end end end context "when the action is delete_key" do before (:all) do reset_registry create_deletable_keys end it "takes no action if the specified key path does not exist in the system" do expect(@registry.key_exists?(reg_parent + '\Osirian')).to eq(false) @new_resource.key(reg_parent + '\Osirian') @new_resource.recursive(false) @new_resource.run_action(:delete_key) expect(@registry.key_exists?(reg_parent + '\Osirian')).to eq(false) end it "deletes key if it has no subkeys and recursive == false" do @new_resource.key(reg_parent + '\OpscodeTest') @new_resource.recursive(false) @new_resource.run_action(:delete_key) expect(@registry.key_exists?(reg_parent + '\OpscodeTest')).to eq(false) end it "raises an exception if the key has subkeys and recursive == false" do @new_resource.key(reg_parent) @new_resource.recursive(false) expect{@new_resource.run_action(:delete_key)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) end it "ignores the values under a key" do @new_resource.key(reg_parent + '\OpscodeIgnoredValues') #@new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}]) @new_resource.recursive(true) @new_resource.run_action(:delete_key) end it "deletes the key if it has subkeys and recursive == true" do @new_resource.key(reg_parent + '\Opscode') @new_resource.recursive(true) @new_resource.run_action(:delete_key) expect(@registry.key_exists?(reg_parent + '\Opscode')).to eq(false) end it "prepares the reporting data for action :delete_key" do @new_resource.key(reg_parent + '\ReportKey') @new_resource.recursive(true) @new_resource.run_action(:delete_key) @report = @resource_reporter.prepare_run_data expect(@report["action"]).to eq("end") expect(@report["resources"][0]["type"]).to eq("registry_key") expect(@report["resources"][0]["name"]).to eq(resource_name) expect(@report["resources"][0]["id"]).to eq(reg_parent + '\ReportKey') #Not testing for before or after values to match since #after -> new_resource.values and #before -> current_resource.values expect(@report["resources"][0]["result"]).to eq("delete_key") expect(@report["status"]).to eq("success") expect(@report["total_res_count"]).to eq("1") end context "while running in whyrun mode" do before (:each) do Chef::Config[:why_run] = true end it "does not throw an exception if the key has subkeys but recursive is set to false" do @new_resource.key(reg_parent + '\OpscodeWhyRun') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:delete_key) end it "does nothing if the action is delete_key" do @new_resource.key(reg_parent + '\OpscodeWhyRun') @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}]) @new_resource.recursive(false) @new_resource.run_action(:delete_key) expect(@registry.key_exists?(reg_parent + '\OpscodeWhyRun')).to eq(true) end end end end chef-12.3.0/spec/functional/resource/base.rb0000644000004100000410000000175312520074675020732 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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. # def run_context @run_context ||= begin node = Chef::Node.new node.default[:platform] = ohai[:platform] node.default[:platform_version] = ohai[:platform_version] node.default[:os] = ohai[:os] events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, events) end end chef-12.3.0/spec/functional/resource/template_spec.rb0000644000004100000410000001344612520074675022647 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'spec_helper' describe Chef::Resource::Template do def binread(file) File.open(file,"rb") {|f| f.read } end include_context Chef::Resource::File let(:file_base) { "template_spec" } let(:expected_content) { "slappiness is a warm gun" } let(:node) do node = Chef::Node.new node.normal[:slappiness] = "a warm gun" node end def create_resource cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, cookbook_collection, events) resource = Chef::Resource::Template.new(path, run_context) resource.source('openldap_stuff.conf.erb') resource.cookbook('openldap') # NOTE: partials rely on `cookbook_name` getting set by chef internals and # ignore the user-set `cookbook` attribute. resource.cookbook_name = "openldap" resource end let(:resource) do create_resource end let(:default_mode) { (0666 & ~File.umask).to_s(8) } it_behaves_like "a file resource" it_behaves_like "a securable resource with reporting" context "when the target file does not exist" do it "creates the template with the rendered content using the variable attribute when the :create action is run" do resource.source('openldap_variable_stuff.conf.erb') resource.variables(:secret => "nutella") resource.run_action(:create) expect(IO.read(path)).to eq("super secret is nutella") end it "creates the template with the rendered content using a local erb file when the :create action is run" do resource.source(File.expand_path(File.join(CHEF_SPEC_DATA,'cookbooks','openldap','templates','default','openldap_stuff.conf.erb'))) resource.cookbook(nil) resource.local(true) resource.run_action(:create) expect(IO.read(path)).to eq(expected_content) end end describe "when the template resource defines helper methods" do include_context "diff disabled" let(:resource) do r = create_resource r.source "helper_test.erb" r end let(:expected_content) { "value from helper method" } shared_examples "a template with helpers" do it "generates expected content by calling helper methods" do resource.run_action(:create) expect(binread(path).strip).to eq(expected_content) end end context "using single helper syntax" do before do resource.helper(:helper_method) { "value from helper method" } end it_behaves_like "a template with helpers" end context "using single helper syntax referencing @node" do before do node.set[:helper_test_attr] = "value from helper method" resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" } end it_behaves_like "a template with helpers" end context "using an inline block to define helpers" do before do resource.helpers do def helper_method "value from helper method" end end end it_behaves_like "a template with helpers" end context "using an inline block referencing @node" do before do node.set[:helper_test_attr] = "value from helper method" resource.helpers do def helper_method @node[:helper_test_attr] end end end it_behaves_like "a template with helpers" end context "using a module from a library" do module ExampleModule def helper_method "value from helper method" end end before do resource.helpers(ExampleModule) end it_behaves_like "a template with helpers" end context "using a module from a library referencing @node" do module ExampleModuleReferencingATNode def helper_method @node[:helper_test_attr] end end before do node.set[:helper_test_attr] = "value from helper method" resource.helpers(ExampleModuleReferencingATNode) end it_behaves_like "a template with helpers" end context "using helpers with partial templates" do before do resource.source("helpers_via_partial_test.erb") resource.helper(:helper_method) { "value from helper method" } end it_behaves_like "a template with helpers" end end describe "when template source contains windows style line endings" do include_context "diff disabled" ["all", "some", "no"].each do |test_case| context "for #{test_case} lines" do let(:resource) do r = create_resource r.source "#{test_case}_windows_line_endings.erb" r end it "output should contain platform's line endings" do resource.run_action(:create) binread(path).each_line do |line| expect(line).to end_with(Chef::Platform.windows? ? "\r\n" : "\n") end end end end end end chef-12.3.0/spec/functional/resource/ohai_spec.rb0000644000004100000410000000333312520074675021746 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 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::Ohai do let(:ohai) { o = Ohai::System.new o.all_plugins o } let(:node) { Chef::Node.new } let(:run_context) { node.default[:platform] = ohai[:platform] node.default[:platform_version] = ohai[:platform_version] events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, events) } shared_examples_for "reloaded :uptime" do it "should reload :uptime" do initial_uptime = ohai[:uptime] # Sleep for a second so the uptime gets updated. sleep 1 ohai_resource.run_action(:reload) expect(node[:uptime]).not_to eq(initial_uptime) end end describe "when reloading all plugins" do let(:ohai_resource) { Chef::Resource::Ohai.new("reload all", run_context)} it_behaves_like "reloaded :uptime" end describe "when reloading only uptime" do let(:ohai_resource) { r = Chef::Resource::Ohai.new("reload all", run_context) r.plugin("uptime") r } it_behaves_like "reloaded :uptime" end end chef-12.3.0/spec/functional/resource/powershell_spec.rb0000644000004100000410000004675712520074675023233 0ustar www-datawww-data# # Author:: Adam Edwards () # 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::WindowsScript::PowershellScript, :windows_only do include_context Chef::Resource::WindowsScript let (:architecture_command) { 'echo $env:PROCESSOR_ARCHITECTURE' } let (:output_command) { ' | out-file -encoding ASCII ' } it_behaves_like "a Windows script running on Windows" let(:successful_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" } let(:failed_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe /badargument" } let(:processor_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTURE" } let(:native_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTUREW6432" } let(:cmdlet_exit_code_not_found_content) { "get-item '.\\thisdoesnotexist'" } let(:cmdlet_exit_code_success_content) { "get-item ." } let(:windows_process_exit_code_success_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" } let(:windows_process_exit_code_not_found_content) { "findstr /notavalidswitch" } # Note that process exit codes on 32-bit Win2k3 cannot # exceed maximum value of signed integer let(:arbitrary_nonzero_process_exit_code) { 4193 } let(:arbitrary_nonzero_process_exit_code_content) { "exit #{arbitrary_nonzero_process_exit_code}" } let(:invalid_powershell_interpreter_flag) { "/thisflagisinvalid" } let(:valid_powershell_interpreter_flag) { "-Sta" } let!(:resource) do r = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context) r.code(successful_executable_script_content) r end describe "when the run action is invoked on Windows" do it "successfully executes a non-cmdlet Windows binary as the last command of the script" do resource.code(successful_executable_script_content + " | out-file -encoding ASCII #{script_output_path}") resource.returns(0) resource.run_action(:run) end it "returns the -27 for a powershell script that exits with -27", :windows_powershell_dsc_only do # This is broken on Powershell < 4.0 file = Tempfile.new(['foo', '.ps1']) begin file.write "exit -27" file.close resource.code(". \"#{file.path}\"") resource.returns(-27) resource.run_action(:run) ensure file.close file.unlink end end it "returns the process exit code" do resource.code(arbitrary_nonzero_process_exit_code_content) resource.returns(arbitrary_nonzero_process_exit_code) resource.run_action(:run) end it "returns 0 if the last command was a cmdlet that succeeded" do resource.code(cmdlet_exit_code_success_content) resource.returns(0) resource.run_action(:run) end it "returns 0 if the last command was a cmdlet that succeeded and was preceded by a non-cmdlet Windows binary that failed" do resource.code([windows_process_exit_code_not_found_content, cmdlet_exit_code_success_content].join(';')) resource.returns(0) resource.run_action(:run) end it "returns 1 if the last command was a cmdlet that failed" do resource.code(cmdlet_exit_code_not_found_content) resource.returns(1) resource.run_action(:run) end it "returns 1 if the last command was a cmdlet that failed and was preceded by a successfully executed non-cmdlet Windows binary" do resource.code([windows_process_exit_code_success_content, cmdlet_exit_code_not_found_content].join(';')) resource.returns(1) resource.run_action(:run) end # This somewhat ambiguous case, two failures of different types, # seems to violate the principle of returning the status of the # last line executed -- in this case, we return the status of the # second to last line. This happens because Powershell gives no # way for us to determine whether the last operation was a cmdlet # or Windows process. Because the latter gives more specific # errors than 0 or 1, we return that instead, which is acceptable # since callers can test for nonzero rather than testing for 1. it "returns 1 if the last command was a cmdlet that failed and was preceded by an unsuccessfully executed non-cmdlet Windows binary" do resource.code([arbitrary_nonzero_process_exit_code_content,cmdlet_exit_code_not_found_content].join(';')) resource.returns(arbitrary_nonzero_process_exit_code) resource.run_action(:run) end it "returns 0 if the last command was a non-cmdlet Windows binary that succeeded and was preceded by a failed cmdlet" do resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(';')) resource.returns(arbitrary_nonzero_process_exit_code) resource.run_action(:run) end it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that succeeded" do resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(';')) resource.returns(arbitrary_nonzero_process_exit_code) resource.run_action(:run) end it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that failed" do resource.code([cmdlet_exit_code_not_found_content, arbitrary_nonzero_process_exit_code_content].join(';')) resource.returns(arbitrary_nonzero_process_exit_code) resource.run_action(:run) end it "returns 0 for $false as the last line of the script when convert_boolean_return is false" do resource.code "$false" resource.returns(0) resource.run_action(:run) end it "returns 0 for $true as the last line of the script when convert_boolean_return is false" do resource.code "$true" resource.returns(0) resource.run_action(:run) end it "returns 1 for $false as the last line of the script when convert_boolean_return is true" do resource.convert_boolean_return true resource.code "$false" resource.returns(1) resource.run_action(:run) end it "returns 0 for $true as the last line of the script when convert_boolean_return is true" do resource.convert_boolean_return true resource.code "$true" resource.returns(0) resource.run_action(:run) end it "executes a script with a 64-bit process on a 64-bit OS, otherwise a 32-bit process" do resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}") resource.returns(0) resource.run_action(:run) is_64_bit = (ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64') || (ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64') detected_64_bit = source_contains_case_insensitive_content?( get_script_output, 'AMD64' ) expect(is_64_bit).to eq(detected_64_bit) end it "returns 1 if an invalid flag is passed to the interpreter" do resource.code(cmdlet_exit_code_success_content) resource.flags(invalid_powershell_interpreter_flag) resource.returns(1) resource.run_action(:run) end it "returns 0 if a valid flag is passed to the interpreter" do resource.code(cmdlet_exit_code_success_content) resource.flags(valid_powershell_interpreter_flag) resource.returns(0) resource.run_action(:run) end it "raises an error when given a block and a guard_interpreter" do resource.guard_interpreter :sh resource.only_if { true } expect { resource.should_skip?(:run) }.to raise_error(ArgumentError, /guard_interpreter does not support blocks/) end end context "when running on a 32-bit version of Windows", :windows32_only do it "executes a script with a 32-bit process if process architecture :i386 is specified" do resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}") resource.architecture(:i386) resource.returns(0) resource.run_action(:run) expect(source_contains_case_insensitive_content?( get_script_output, 'x86' )).to eq(true) end it "raises an exception if :x86_64 process architecture is specified" do begin expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect rescue Chef::Exceptions::Win32ArchitectureIncorrect end end end context "when running on a 64-bit version of Windows", :windows64_only do it "executes a script with a 64-bit process if :x86_64 arch is specified" do resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}") resource.architecture(:x86_64) resource.returns(0) resource.run_action(:run) expect(source_contains_case_insensitive_content?( get_script_output, 'AMD64' )).to eq(true) end it "executes a script with a 32-bit process if :i386 arch is specified" do resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}") resource.architecture(:i386) resource.returns(0) resource.run_action(:run) expect(source_contains_case_insensitive_content?( get_script_output, 'x86' )).to eq(true) end end describe "when executing guards" do before(:each) do resource.not_if.clear resource.only_if.clear end context "when the guard_interpreter's default value of :powershell_script is overridden to :default" do before(:each) do resource.guard_interpreter :default end it "evaluates a succeeding not_if block using cmd.exe as false by default" do resource.not_if "exit /b 0" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a failing not_if block using cmd.exe as true by default" do resource.not_if "exit /b 2" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates an succeeding only_if block using cmd.exe as true by default" do resource.only_if "exit /b 0" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a failing only_if block using cmd.exe as false by default" do resource.only_if "exit /b 2" expect(resource.should_skip?(:run)).to be_truthy end end context "the only_if is specified before the guard" do before do resource.guard_interpreter :default end it "evaluates a powershell $true for a only_if block as true" do resource.only_if "$true" resource.guard_interpreter :powershell_script expect(resource.should_skip?(:run)).to be_falsey end end context "with powershell_script as the guard_interpreter" do it "has a guard_interpreter attribute set to :powershell_script" do expect(resource.guard_interpreter).to eq(:powershell_script) end it "evaluates a powershell $false for a not_if block as true" do resource.not_if "$false" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a powershell $true for a not_if block as false" do resource.not_if "$true" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a powershell $false for an only_if block as false" do resource.only_if "$false" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a powershell $true for a only_if block as true" do resource.only_if "$true" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a not_if block using powershell.exe" do resource.not_if "exit([int32](![System.Environment]::CommandLine.Contains('powershell.exe')))" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates an only_if block using powershell.exe" do resource.only_if "exit([int32](![System.Environment]::CommandLine.Contains('powershell.exe')))" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a non-zero powershell exit status for not_if as true" do resource.not_if "exit 37" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a zero powershell exit status for not_if as false" do resource.not_if "exit 0" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a failed executable exit status for not_if as false" do resource.not_if windows_process_exit_code_not_found_content expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a successful executable exit status for not_if as true" do resource.not_if windows_process_exit_code_success_content expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a failed executable exit status for only_if as false" do resource.only_if windows_process_exit_code_not_found_content expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a successful executable exit status for only_if as true" do resource.only_if windows_process_exit_code_success_content expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a failed cmdlet exit status for not_if as true" do resource.not_if "throw 'up'" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a successful cmdlet exit status for not_if as true" do resource.not_if "cd ." expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a failed cmdlet exit status for only_if as false" do resource.only_if "throw 'up'" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a successful cmdlet exit status for only_if as true" do resource.only_if "cd ." expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a not_if block using the cwd guard parameter" do custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc" resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd expect(resource.should_skip?(:run)).to be_truthy end it "evaluates an only_if block using the cwd guard parameter" do custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc" resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')", :cwd => custom_cwd expect(resource.should_skip?(:run)).to be_falsey end it "inherits cwd from the parent resource for only_if" do custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc" resource.cwd custom_cwd resource.only_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')" expect(resource.should_skip?(:run)).to be_falsey end it "inherits cwd from the parent resource for not_if" do custom_cwd = "#{ENV['SystemRoot']}\\system32\\drivers\\etc" resource.cwd custom_cwd resource.not_if "exit ! [int32]($pwd.path -eq '#{custom_cwd}')" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean false as zero status code", :windows64_only do resource.architecture :x86_64 resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'AMD64')" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean true as nonzero status code", :windows64_only do resource.architecture :x86_64 resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'AMD64')" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code" do resource.architecture :i386 resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'X86')" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code" do resource.architecture :i386 resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'X86')" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for only_if" do resource.convert_boolean_return true resource.only_if "$false" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for not_if" do resource.convert_boolean_return true resource.not_if "$false" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a simple boolean true as 0 status code when convert_boolean_return is true for only_if" do resource.convert_boolean_return true resource.only_if "$true" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a simple boolean true as 0 status code when convert_boolean_return is true for not_if" do resource.convert_boolean_return true resource.not_if "$true" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if" do resource.convert_boolean_return true resource.architecture :i386 resource.only_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if" do resource.convert_boolean_return true resource.architecture :i386 resource.not_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'" expect(resource.should_skip?(:run)).to be_falsey end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if" do resource.convert_boolean_return true resource.architecture :i386 resource.only_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'" expect(resource.should_skip?(:run)).to be_truthy end it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if" do resource.convert_boolean_return true resource.architecture :i386 resource.not_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'" expect(resource.should_skip?(:run)).to be_truthy end end end def get_script_output script_output = File.read(script_output_path) end def source_contains_case_insensitive_content?( source, content ) source.downcase.include?(content.downcase) end end chef-12.3.0/spec/functional/resource/bff_spec.rb0000644000004100000410000000650712520074675021571 0ustar www-datawww-data# # Author:: Prabhu Das () # 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 'functional/resource/base' require 'chef/mixin/shell_out' # Run the test only for AIX platform. describe Chef::Resource::BffPackage, :requires_root, :external => ohai[:platform] != 'aix' do include Chef::Mixin::ShellOut let(:new_resource) do new_resource = Chef::Resource::BffPackage.new(@pkg_name, run_context) new_resource.source @pkg_path new_resource end def bff_pkg_should_be_installed(resource) expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(0) ::File.exists?("/usr/PkgA/bin/acommand") end def bff_pkg_should_be_removed(resource) expect(shell_out("lslpp -L #{resource.name}").exitstatus).to eq(1) !::File.exists?("/usr/PkgA/bin/acommand") end before(:all) do @pkg_name = "PkgA.rte" @pkg_path = "/tmp/PkgA.1.0.0.0.bff" FileUtils.cp 'spec/functional/assets/PkgA.1.0.0.0.bff' , @pkg_path end after(:all) do FileUtils.rm @pkg_path end context "package install action" do it "should install a package" do new_resource.run_action(:install) bff_pkg_should_be_installed(new_resource) end after(:each) do shell_out("installp -u #{@pkg_name}") end end context "package install action with options" do it "should install a package" do new_resource.options("-e/tmp/installp.log") new_resource.run_action(:install) bff_pkg_should_be_installed(new_resource) end after(:each) do shell_out("installp -u #{@pkg_name}") FileUtils.rm "/tmp/installp.log" end end context "package upgrade action" do before(:each) do shell_out("installp -aYF -d #{@pkg_path} #{@pkg_name}") @pkg_path = "/tmp/PkgA.2.0.0.0.bff" FileUtils.cp 'spec/functional/assets/PkgA.2.0.0.0.bff' , @pkg_path end it "should upgrade package" do new_resource.run_action(:install) bff_pkg_should_be_installed(new_resource) end after(:each) do shell_out("installp -u #{@pkg_name}") FileUtils.rm @pkg_path end end context "package remove action" do before(:each) do shell_out("installp -aYF -d #{@pkg_path} #{@pkg_name}") end it "should remove an installed package" do new_resource.run_action(:remove) bff_pkg_should_be_removed(new_resource) end end context "package remove action with options" do before(:each) do shell_out("installp -aYF -d #{@pkg_path} #{@pkg_name}") end it "should remove an installed package" do new_resource.options("-e/tmp/installp.log") new_resource.run_action(:remove) bff_pkg_should_be_removed(new_resource) end after(:each) do FileUtils.rm "/tmp/installp.log" end end end chef-12.3.0/spec/functional/version_spec.rb0000644000004100000410000000234012520074675020661 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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 File.expand_path('../../spec_helper', __FILE__) require 'chef/mixin/shell_out' require 'chef/version' require 'ohai/version' describe "Chef Versions" do include Chef::Mixin::ShellOut let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..") } binaries = [ "chef-client", "chef-shell", "chef-apply", "knife", "chef-solo" ] binaries.each do |binary| it "#{binary} version should be sane" do expect(shell_out!("ruby #{File.join("bin", binary)} -v", :cwd => chef_dir).stdout.chomp).to include("Chef: #{Chef::VERSION}") end end end chef-12.3.0/spec/functional/notifications_spec.rb0000644000004100000410000001413412520074675022051 0ustar www-datawww-datarequire 'spec_helper' require 'chef/recipe' # The goal of these tests is to make sure that loading resources from a file creates the necessary notifications. # Then once converge has started, both immediate and delayed notifications are called as the resources are converged. # We want to do this WITHOUT actually converging any resources - we don't want to take time changing the system, # we just want to make sure the run_context, the notification DSL and the converge hooks are working together # to perform notifications. # This test is extremely fragile since it mocks MANY different systems at once - any of them changes, this test # breaks describe "Notifications" do # We always pretend we are on OSx because that has a specific provider (HomebrewProvider) so it # tests the translation from Provider => HomebrewProvider let(:node) { n = Chef::Node.new n.override[:os] = "darwin" n } let(:cookbook_collection) { double("Chef::CookbookCollection").as_null_object } let(:events) { double("Chef::EventDispatch::Dispatcher").as_null_object } let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) } let(:recipe) { Chef::Recipe.new("notif", "test", run_context) } let(:runner) { Chef::Runner.new(run_context) } before do # By default, every provider will do nothing p = Chef::Provider.new(nil, run_context) allow_any_instance_of(Chef::Resource).to receive(:provider_for_action).and_return(p) allow(p).to receive(:run_action) end it "should subscribe from one resource to another" do log_resource = recipe.declare_resource(:log, "subscribed-log") do message "This is a log message" action :nothing subscribes :write, "package[vim]", :immediately end package_resource = recipe.declare_resource(:package, "vim") do action :install end expect(log_resource).to receive(:run_action).with(:nothing, nil, nil).and_call_original expect(package_resource).to receive(:run_action).with(:install, nil, nil).and_call_original update_action(package_resource) expect(log_resource).to receive(:run_action).with(:write, :immediate, package_resource).and_call_original runner.converge end it "should notify from one resource to another immediately" do log_resource = recipe.declare_resource(:log, "log") do message "This is a log message" action :write notifies :install, "package[vim]", :immediately end package_resource = recipe.declare_resource(:package, "vim") do action :nothing end expect(log_resource).to receive(:run_action).with(:write, nil, nil).and_call_original update_action(log_resource) expect(package_resource).to receive(:run_action).with(:install, :immediate, log_resource).ordered.and_call_original expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original runner.converge end it "should notify from one resource to another delayed" do log_resource = recipe.declare_resource(:log, "log") do message "This is a log message" action :write notifies :install, "package[vim]", :delayed end package_resource = recipe.declare_resource(:package, "vim") do action :nothing end expect(log_resource).to receive(:run_action).with(:write, nil, nil).and_call_original update_action(log_resource) expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original expect(package_resource).to receive(:run_action).with(:install, :delayed, nil).ordered.and_call_original runner.converge end describe "when one resource is defined lazily" do it "subscribes to a resource defined in a ruby block" do r = recipe t = self ruby_block = recipe.declare_resource(:ruby_block, "rblock") do block do log_resource = r.declare_resource(:log, "log") do message "This is a log message" action :write end t.expect(log_resource).to t.receive(:run_action).with(:write, nil, nil).and_call_original t.update_action(log_resource) end end package_resource = recipe.declare_resource(:package, "vim") do action :nothing subscribes :install, "log[log]", :delayed end # RubyBlock needs to be able to run for our lazy examples to work - and it alone cannot affect the system expect(ruby_block).to receive(:provider_for_action).and_call_original expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original expect(package_resource).to receive(:run_action).with(:install, :delayed, nil).ordered.and_call_original runner.converge end it "notifies from inside a ruby_block to a resource defined outside" do r = recipe t = self ruby_block = recipe.declare_resource(:ruby_block, "rblock") do block do log_resource = r.declare_resource(:log, "log") do message "This is a log message" action :write notifies :install, "package[vim]", :immediately end t.expect(log_resource).to t.receive(:run_action).with(:write, nil, nil).and_call_original t.update_action(log_resource) end end package_resource = recipe.declare_resource(:package, "vim") do action :nothing end # RubyBlock needs to be able to run for our lazy examples to work - and it alone cannot affect the system expect(ruby_block).to receive(:provider_for_action).and_call_original expect(package_resource).to receive(:run_action).with(:install, :immediate, instance_of(Chef::Resource::Log)).ordered.and_call_original expect(package_resource).to receive(:run_action).with(:nothing, nil, nil).ordered.and_call_original runner.converge end end # Mocks having the provider run successfully and update the resource def update_action(resource) p = Chef::Provider.new(resource, run_context) expect(resource).to receive(:provider_for_action).and_return(p) expect(p).to receive(:run_action) { resource.updated_by_last_action(true) } end end chef-12.3.0/spec/functional/win32/0000755000004100000410000000000012520074675016600 5ustar www-datawww-datachef-12.3.0/spec/functional/win32/versions_spec.rb0000644000004100000410000001031512520074675022007 0ustar www-datawww-data# # Author:: Chirag Jog () # 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' if Chef::Platform.windows? require 'chef/win32/version' end describe "Chef::ReservedNames::Win32::Version", :windows_only, :not_supported_on_win2k3 do before do wmi = WmiLite::Wmi.new host = wmi.first_of('Win32_OperatingSystem') # Use WMI to determine current OS version. # On Win2k8R2 and later, we can dynamically obtain marketing # names for comparison from WMI so the test should not # need to be modified when new Windows releases arise. # For Win2k3 and Win2k8, we use static names in this test # based on the version number information from WMI. The names # from WMI contain extended characters such as registered # trademark on Win2k8 and Win2k3 that we're not using in our # library, so we have to set the expectation statically. if Chef::Platform::windows_server_2003? @current_os_version = 'Windows Server 2003 R2' elsif is_windows_server_2008?(host) @current_os_version = 'Windows Server 2008' else # The name from WMI is actually what we want in Win2k8R2+. # So this expectation sould continue to hold without modification # as new versions of Windows are released. @current_os_version = host['caption'] end @version = Chef::ReservedNames::Win32::Version.new end def for_each_windows_version(&block) @version.methods.each do |method_name| if Chef::ReservedNames::Win32::Version::WIN_VERSIONS.keys.find { | key | method_name.to_s == Chef::ReservedNames::Win32::Version.send(:method_name_from_marketing_name,key) } yield method_name end end end context "Win32 version object" do it "should have have one method for each marketing version" do versions = 0 for_each_windows_version { versions += 1 } expect(versions).to be > 0 expect(versions).to eq(Chef::ReservedNames::Win32::Version::WIN_VERSIONS.length) end it "should only contain version methods with legal method names" do method_name_pattern = /[a-z]+([a-z]|[0-9]|_)*\?{0,1}/ for_each_windows_version do |method_name| method_match = method_name_pattern.match(method_name.to_s) expect(method_match).not_to be_nil expect(method_name.to_s).to eq(method_match[0]) end end it "should have exactly one method that returns true" do true_versions = 0 for_each_windows_version do |method_name| true_versions += 1 if @version.send(method_name) end expect(true_versions).to eq(1) end it "should successfully execute all version methods" do for_each_windows_version { |method_name| @version.send(method_name.to_sym) } end end context "Windows Operating System version" do it "should match the version from WMI" do expect(@current_os_version).to include(@version.marketing_name) end end def is_windows_server_2008?(wmi_host) is_win2k8 = false os_version = wmi_host['version'] # The operating system version is a string in the following form # that can be split into components based on the '.' delimiter: # MajorVersionNumber.MinorVersionNumber.BuildNumber os_version_components = os_version.split('.') if os_version_components.length < 2 raise 'WMI returned a Windows version from Win32_OperatingSystem.Version ' + 'with an unexpected format. The Windows version could not be determined.' end # Windows 6.0 is Windows Server 2008, so test the major and # minor version components is_win2k8 = os_version_components[0] == '6' && os_version_components[1] == '0' end end chef-12.3.0/spec/functional/win32/security_spec.rb0000644000004100000410000000722512520074675022014 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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' if Chef::Platform.windows? require 'chef/win32/security' end describe 'Chef::Win32::Security', :windows_only do it "has_admin_privileges? returns true when running as admin" do expect(Chef::ReservedNames::Win32::Security.has_admin_privileges?).to eq(true) end # We've done some investigation adding a negative test and it turned # out to be a lot of work since mixlib-shellout doesn't have user # support for windows. # # TODO - Add negative tests once mixlib-shellout has user support it "has_admin_privileges? returns false when running as non-admin" do skip "requires user support in mixlib-shellout" end describe 'get_file_security' do it 'should return a security descriptor when called with a path that exists' do security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security( "C:\\Program Files") # Make sure the security descriptor works expect(security_descriptor.dacl_present?).to be true end end describe 'access_check' do let(:security_descriptor) { Chef::ReservedNames::Win32::Security.get_file_security( "C:\\Program Files") } let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS } let(:token) { Chef::ReservedNames::Win32::Security.open_process_token( Chef::ReservedNames::Win32::Process.get_current_process, token_rights).duplicate_token(:SecurityImpersonation) } let(:mapping) { mapping = Chef::ReservedNames::Win32::Security::GENERIC_MAPPING.new mapping[:GenericRead] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ mapping[:GenericWrite] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_WRITE mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS mapping } let(:desired_access) { Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ } it 'should check if the provided token has the desired access' do expect(Chef::ReservedNames::Win32::Security.access_check(security_descriptor, token, desired_access, mapping)).to be true end end describe 'Chef::Win32::Security::Token' do let(:token) { Chef::ReservedNames::Win32::Security.open_process_token( Chef::ReservedNames::Win32::Process.get_current_process, token_rights) } context 'with all rights' do let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS } it 'can duplicate a token' do expect{ token.duplicate_token(:SecurityImpersonation) }.not_to raise_error end end context 'with read only rights' do let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_READ } it 'raises an exception when trying to duplicate' do expect{ token.duplicate_token(:SecurityImpersonation) }.to raise_error(Chef::Exceptions::Win32APIError) end end end end chef-12.3.0/spec/functional/win32/registry_helper_spec.rb0000644000004100000410000007424112520074675023356 0ustar www-datawww-data# # Author:: Prajakta Purohit () # Author:: Lamont Granquist () # 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/win32/registry' describe Chef::Resource::RegistryKey, :unix_only do before(:all) do events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins node.consume_external_attrs(ohai.data,{}) run_context = Chef::RunContext.new(node, {}, events) @resource = Chef::Resource::RegistryKey.new("HKCU\\Software", run_context) end context "when load_current_resource is run on a non-windows node" do it "throws an exception because you don't have a windows registry (derp)" do @resource.key("HKCU\\Software\\Opscode") @resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}]) expect{@resource.run_action(:create)}.to raise_error(Chef::Exceptions::Win32NotWindows) end end end describe 'Chef::Win32::Registry', :windows_only do before(:all) do #Create a registry item ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root" ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch" ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Flower" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['RootType1', Win32::Registry::REG_SZ] = 'fibrous' reg.write('Roots', Win32::Registry::REG_MULTI_SZ, ["strong roots", "healthy tree"]) end ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['Strong', Win32::Registry::REG_SZ] = 'bird nest' end ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch\\Flower', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['Petals', Win32::Registry::REG_MULTI_SZ] = ["Pink", "Delicate"] end #Create the node with ohai data events = Chef::EventDispatch::Dispatcher.new @node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins @node.consume_external_attrs(ohai.data,{}) @run_context = Chef::RunContext.new(@node, {}, events) #Create a registry object that has access ot the node previously created @registry = Chef::Win32::Registry.new(@run_context) end #Delete what is left of the registry key-values previously created after(:all) do ::Win32::Registry::HKEY_CURRENT_USER.open("Software") do |reg| reg.delete_key("Root", true) end end # Server Versions # it "succeeds if server versiion is 2003R2, 2008, 2008R2, 2012" do # end # it "falis if the server versions are anything else" do # end describe "hive_exists?" do it "returns true if the hive exists" do expect(@registry.hive_exists?("HKCU\\Software\\Root")).to eq(true) end it "returns false if the hive does not exist" do hive = expect(@registry.hive_exists?("LYRU\\Software\\Root")).to eq(false) end end describe "key_exists?" do it "returns true if the key path exists" do expect(@registry.key_exists?("HKCU\\Software\\Root\\Branch\\Flower")).to eq(true) end it "returns false if the key path does not exist" do expect(@registry.key_exists?("HKCU\\Software\\Branch\\Flower")).to eq(false) end it "throws an exception if the hive does not exist" do expect {@registry.key_exists?("JKLM\\Software\\Branch\\Flower")}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "key_exists!" do it "returns true if the key path exists" do expect(@registry.key_exists!("HKCU\\Software\\Root\\Branch\\Flower")).to eq(true) end it "throws an exception if the key path does not exist" do expect {@registry.key_exists!("HKCU\\Software\\Branch\\Flower")}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "throws an exception if the hive does not exist" do expect {@registry.key_exists!("JKLM\\Software\\Branch\\Flower")}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "value_exists?" do it "throws an exception if the hive does not exist" do expect {@registry.value_exists?("JKLM\\Software\\Branch\\Flower", {:name=>"Petals"})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "throws an exception if the key does not exist" do expect {@registry.value_exists?("HKCU\\Software\\Branch\\Flower", {:name=>"Petals"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if the value exists" do expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true) end it "returns false if the value does not exist" do expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})).to eq(false) end end describe "value_exists!" do it "throws an exception if the hive does not exist" do expect {@registry.value_exists!("JKLM\\Software\\Branch\\Flower", {:name=>"Petals"})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "throws an exception if the key does not exist" do expect {@registry.value_exists!("HKCU\\Software\\Branch\\Flower", {:name=>"Petals"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if the value exists" do expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true) end it "throws an exception if the value does not exist" do expect {@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})}.to raise_error(Chef::Exceptions::Win32RegValueMissing) end end describe "data_exists?" do it "throws an exception if the hive does not exist" do expect {@registry.data_exists?("JKLM\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "throws an exception if the key does not exist" do expect {@registry.data_exists?("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if all the data matches" do expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true) end it "returns false if the name does not exist" do expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(false) end it "returns false if the types do not match" do expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Pink"})).to eq(false) end it "returns false if the data does not match" do expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Mauve", "Delicate"]})).to eq(false) end end describe "data_exists!" do it "throws an exception if the hive does not exist" do expect {@registry.data_exists!("JKLM\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "throws an exception if the key does not exist" do expect {@registry.data_exists!("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if all the data matches" do expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true) end it "throws an exception if the name does not exist" do expect {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegDataMissing) end it "throws an exception if the types do not match" do expect {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Pink"})}.to raise_error(Chef::Exceptions::Win32RegDataMissing) end it "throws an exception if the data does not match" do expect {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Mauve", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegDataMissing) end end describe "get_values" do it "returns all values for a key if it exists" do values = @registry.get_values("HKCU\\Software\\Root") expect(values).to be_an_instance_of Array expect(values).to eq([{:name=>"RootType1", :type=>:string, :data=>"fibrous"}, {:name=>"Roots", :type=>:multi_string, :data=>["strong roots", "healthy tree"]}]) end it "throws an exception if the key does not exist" do expect {@registry.get_values("HKCU\\Software\\Branch\\Flower")}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "throws an exception if the hive does not exist" do expect {@registry.get_values("JKLM\\Software\\Branch\\Flower")}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "set_value" do it "updates a value if the key, value exist and type matches and value different" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(true) end it "updates a value if the type does match and the values are different" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Yellow"})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Yellow"})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(false) end it "creates a value if key exists and value does not" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(true) end it "does nothing if data,type and name parameters for the value are same" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(false) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})).to eq(true) end it "throws an exception if the key does not exist" do expect {@registry.set_value("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "throws an exception if the hive does not exist" do expect {@registry.set_value("JKLM\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end # we are validating that the data gets .to_i called on it when type is a :dword it "casts an integer string given as a dword into an integer" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe32767", :type=>:dword, :data=>"32767"})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe32767", :type=>:dword, :data=>32767})).to eq(true) end it "casts a nonsense string given as a dword into zero" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeZero", :type=>:dword, :data=>"whatdoesthisdo"})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeZero", :type=>:dword, :data=>0})).to eq(true) end it "throws an exception when trying to cast an array to an int for a dword" do expect {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldThrow", :type=>:dword, :data=>["one","two"]})}.to raise_error end # we are validating that the data gets .to_s called on it when type is a :string it "stores the string representation of an array into a string if you pass it an array" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBePainful", :type=>:string, :data=>["one","two"]})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBePainful", :type=>:string, :data=>'["one", "two"]'})).to eq(true) end it "stores the string representation of a number into a string if you pass it an number" do expect(@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe65535", :type=>:string, :data=>65535})).to eq(true) expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe65535", :type=>:string, :data=>"65535"})).to eq(true) end # we are validating that the data gets .to_a called on it when type is a :multi_string it "throws an exception when a multi-string is passed a number" do expect {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldThrow", :type=>:multi_string, :data=>65535})}.to raise_error end it "throws an exception when a multi-string is passed a string" do expect {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeWat", :type=>:multi_string, :data=>"foo"})}.to raise_error end end describe "create_key" do before(:all) do ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root") do |reg| begin reg.delete_key("Trunk", true) rescue end end end it "throws an exception if the path has missing keys but recursive set to false" do expect {@registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker")).to eq(false) end it "creates the key_path if the keys were missing but recursive was set to true" do expect(@registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", true)).to eq(true) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker")).to eq(true) end it "does nothing if the key already exists" do expect(@registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", false)).to eq(true) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker")).to eq(true) end it "throws an exception of the hive does not exist" do expect {@registry.create_key("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", false)}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "delete_value" do before(:all) do ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Peck\\Woodpecker" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Trunk\\Peck\\Woodpecker', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['Peter', Win32::Registry::REG_SZ] = 'Tiny' end end it "deletes values if the value exists" do expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})).to eq(true) expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})).to eq(false) end it "does nothing if value does not exist" do expect(@registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})).to eq(true) expect(@registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})).to eq(false) end it "throws an exception if the key does not exist?" do expect {@registry.delete_value("HKCU\\Software\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "throws an exception if the hive does not exist" do expect {@registry.delete_value("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "delete_key" do before (:all) do ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Fruit" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch\\Fruit', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['Apple', Win32::Registry::REG_MULTI_SZ] = ["Red", "Juicy"] end ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Peck\\Woodpecker" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Trunk\\Peck\\Woodpecker', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['Peter', Win32::Registry::REG_SZ] = 'Tiny' end end it "deletes a key if it has no subkeys" do expect(@registry.delete_key("HKCU\\Software\\Root\\Branch\\Fruit", false)).to eq(true) expect(@registry.key_exists?("HKCU\\Software\\Root\\Branch\\Fruit")).to eq(false) end it "throws an exception if key to delete has subkeys and recursive is false" do expect { @registry.delete_key("HKCU\\Software\\Root\\Trunk", false) }.to raise_error(Chef::Exceptions::Win32RegNoRecursive) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker")).to eq(true) end it "deletes a key if it has subkeys and recursive true" do expect(@registry.delete_key("HKCU\\Software\\Root\\Trunk", true)).to eq(true) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk")).to eq(false) end it "does nothing if the key does not exist" do expect(@registry.delete_key("HKCU\\Software\\Root\\Trunk", true)).to eq(true) expect(@registry.key_exists?("HKCU\\Software\\Root\\Trunk")).to eq(false) end it "throws an exception if the hive does not exist" do expect {@registry.delete_key("JKLM\\Software\\Root\\Branch\\Flower", false)}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end end describe "has_subkeys?" do before(:all) do ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk" ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root\\Trunk") do |reg| begin reg.delete_key("Red", true) rescue end end end it "throws an exception if the hive was missing" do expect {@registry.has_subkeys?("LMNO\\Software\\Root")}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "throws an exception if the key is missing" do expect {@registry.has_subkeys?("HKCU\\Software\\Root\\Trunk\\Red")}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "returns true if the key has subkeys" do expect(@registry.has_subkeys?("HKCU\\Software\\Root")).to eq(true) end it "returns false if the key has no subkeys" do ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Red" expect(@registry.has_subkeys?("HKCU\\Software\\Root\\Trunk\\Red")).to eq(false) end end describe "get_subkeys" do it "throws an exception if the key is missing" do expect {@registry.get_subkeys("HKCU\\Software\\Trunk\\Red")}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "throws an exception if the hive does not exist" do expect {@registry.get_subkeys("JKLM\\Software\\Root")}.to raise_error(Chef::Exceptions::Win32RegHiveMissing) end it "returns the array of subkeys for a given key" do subkeys = @registry.get_subkeys("HKCU\\Software\\Root") reg_subkeys = [] ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root", Win32::Registry::KEY_ALL_ACCESS) do |reg| reg.each_key{|name| reg_subkeys << name} end expect(reg_subkeys).to eq(subkeys) end end describe "architecture" do describe "on 32-bit" do before(:all) do @saved_kernel_machine = @node.automatic_attrs[:kernel][:machine] @node.automatic_attrs[:kernel][:machine] = :i386 end after(:all) do @node.automatic_attrs[:kernel][:machine] = @saved_kernel_machine end context "registry constructor" do it "throws an exception if requested architecture is 64bit but running on 32bit" do expect {Chef::Win32::Registry.new(@run_context, :x86_64)}.to raise_error(Chef::Exceptions::Win32RegArchitectureIncorrect) end it "can correctly set the requested architecture to 32-bit" do @r = Chef::Win32::Registry.new(@run_context, :i386) expect(@r.architecture).to eq(:i386) expect(@r.registry_system_architecture).to eq(0x0200) end it "can correctly set the requested architecture to :machine" do @r = Chef::Win32::Registry.new(@run_context, :machine) expect(@r.architecture).to eq(:machine) expect(@r.registry_system_architecture).to eq(0x0200) end end context "architecture setter" do it "throws an exception if requested architecture is 64bit but running on 32bit" do expect {@registry.architecture = :x86_64}.to raise_error(Chef::Exceptions::Win32RegArchitectureIncorrect) end it "sets the requested architecture to :machine if passed :machine" do @registry.architecture = :machine expect(@registry.architecture).to eq(:machine) expect(@registry.registry_system_architecture).to eq(0x0200) end it "sets the requested architecture to 32-bit if passed i386 as a string" do @registry.architecture = :i386 expect(@registry.architecture).to eq(:i386) expect(@registry.registry_system_architecture).to eq(0x0200) end end end describe "on 64-bit" do before(:all) do @saved_kernel_machine = @node.automatic_attrs[:kernel][:machine] @node.automatic_attrs[:kernel][:machine] = :x86_64 end after(:all) do @node.automatic_attrs[:kernel][:machine] = @saved_kernel_machine end context "registry constructor" do it "can correctly set the requested architecture to 32-bit" do @r = Chef::Win32::Registry.new(@run_context, :i386) expect(@r.architecture).to eq(:i386) expect(@r.registry_system_architecture).to eq(0x0200) end it "can correctly set the requested architecture to 64-bit" do @r = Chef::Win32::Registry.new(@run_context, :x86_64) expect(@r.architecture).to eq(:x86_64) expect(@r.registry_system_architecture).to eq(0x0100) end it "can correctly set the requested architecture to :machine" do @r = Chef::Win32::Registry.new(@run_context, :machine) expect(@r.architecture).to eq(:machine) expect(@r.registry_system_architecture).to eq(0x0100) end end context "architecture setter" do it "sets the requested architecture to 64-bit if passed 64-bit" do @registry.architecture = :x86_64 expect(@registry.architecture).to eq(:x86_64) expect(@registry.registry_system_architecture).to eq(0x0100) end it "sets the requested architecture to :machine if passed :machine" do @registry.architecture = :machine expect(@registry.architecture).to eq(:machine) expect(@registry.registry_system_architecture).to eq(0x0100) end it "sets the requested architecture to 32-bit if passed 32-bit" do @registry.architecture = :i386 expect(@registry.architecture).to eq(:i386) expect(@registry.registry_system_architecture).to eq(0x0200) end end end describe "when running on an actual 64-bit server", :windows64_only do before(:all) do begin ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg| reg.delete_key("Trunk", true) end rescue end begin ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg| reg.delete_key("Trunk", true) end rescue end # 64-bit ::Win32::Registry::HKEY_LOCAL_MACHINE.create("Software\\Root\\Mauve", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) ::Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\\Root\\Mauve', Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg| reg['Alert', Win32::Registry::REG_SZ] = 'Universal' end # 32-bit ::Win32::Registry::HKEY_LOCAL_MACHINE.create("Software\\Root\\Poosh", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) ::Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\\Root\\Poosh', Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg| reg['Status', Win32::Registry::REG_SZ] = 'Lost' end end after(:all) do ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg| reg.delete_key("Trunk", true) end ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg| reg.delete_key("Trunk", true) end end describe "key_exists?" do it "does not find 64-bit keys in the 32-bit registry" do @registry.architecture=:i386 expect(@registry.key_exists?("HKLM\\Software\\Root\\Mauve")).to eq(false) end it "finds 32-bit keys in the 32-bit registry" do @registry.architecture=:i386 expect(@registry.key_exists?("HKLM\\Software\\Root\\Poosh")).to eq(true) end it "does not find 32-bit keys in the 64-bit registry" do @registry.architecture=:x86_64 expect(@registry.key_exists?("HKLM\\Software\\Root\\Mauve")).to eq(true) end it "finds 64-bit keys in the 64-bit registry" do @registry.architecture=:x86_64 expect(@registry.key_exists?("HKLM\\Software\\Root\\Poosh")).to eq(false) end end describe "value_exists?" do it "does not find 64-bit values in the 32-bit registry" do @registry.architecture=:i386 expect{@registry.value_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "finds 32-bit values in the 32-bit registry" do @registry.architecture=:i386 expect(@registry.value_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status"})).to eq(true) end it "does not find 32-bit values in the 64-bit registry" do @registry.architecture=:x86_64 expect(@registry.value_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert"})).to eq(true) end it "finds 64-bit values in the 64-bit registry" do @registry.architecture=:x86_64 expect{@registry.value_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "data_exists?" do it "does not find 64-bit keys in the 32-bit registry" do @registry.architecture=:i386 expect{@registry.data_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert", :type=>:string, :data=>"Universal"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end it "finds 32-bit keys in the 32-bit registry" do @registry.architecture=:i386 expect(@registry.data_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status", :type=>:string, :data=>"Lost"})).to eq(true) end it "does not find 32-bit keys in the 64-bit registry" do @registry.architecture=:x86_64 expect(@registry.data_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert", :type=>:string, :data=>"Universal"})).to eq(true) end it "finds 64-bit keys in the 64-bit registry" do @registry.architecture=:x86_64 expect{@registry.data_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status", :type=>:string, :data=>"Lost"})}.to raise_error(Chef::Exceptions::Win32RegKeyMissing) end end describe "create_key" do it "can create a 32-bit only registry key" do @registry.architecture = :i386 expect(@registry.create_key("HKLM\\Software\\Root\\Trunk\\Red", true)).to eq(true) expect(@registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Red")).to eq(true) @registry.architecture = :x86_64 expect(@registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Red")).to eq(false) end it "can create a 64-bit only registry key" do @registry.architecture = :x86_64 expect(@registry.create_key("HKLM\\Software\\Root\\Trunk\\Blue", true)).to eq(true) expect(@registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Blue")).to eq(true) @registry.architecture = :i386 expect(@registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Blue")).to eq(false) end end end end end chef-12.3.0/spec/functional/win32/crypto_spec.rb0000644000004100000410000000350712520074675021464 0ustar www-datawww-data# # Author:: Jay Mundrawala() # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' if Chef::Platform.windows? require 'chef/win32/crypto' end describe 'Chef::ReservedNames::Win32::Crypto', :windows_only do describe '#encrypt' do before(:all) do ohai_reader = Ohai::System.new ohai_reader.all_plugins("platform") new_node = Chef::Node.new new_node.consume_external_attrs(ohai_reader.data,{}) events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(new_node, {}, events) end let (:plaintext) { 'p@assword' } it 'can be decrypted by powershell' do encrypted = Chef::ReservedNames::Win32::Crypto.encrypt(plaintext) resource = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context) resource.code <<-EOF $encrypted = '#{encrypted}' | ConvertTo-SecureString $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted) $plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) if ($plaintext -ne '#{plaintext}') { Write-Error 'Got: ' $plaintext exit 1 } exit 0 EOF resource.returns(0) resource.run_action(:run) end end end chef-12.3.0/spec/functional/win32/service_manager_spec.rb0000644000004100000410000001660212520074675023276 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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' if Chef::Platform.windows? require 'chef/application/windows_service_manager' end # # ATTENTION: # This test creates a windows service for testing purposes and runs it # as Local System (or an otherwise specified user) on windows boxes. # This test will fail if you run the tests inside a Windows VM by # sharing the code from your host since Local System account by # default can't see the mounted partitions. # Run this test by copying the code to a local VM directory or setup # Local System account to see the maunted partitions for the shared # directories. # describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only do include_context "using Win32::Service" context "with invalid service definition" do it "throws an error when initialized with no service definition" do expect { Chef::Application::WindowsServiceManager.new(nil) }.to raise_error(ArgumentError) end it "throws an error with required missing options" do test_service.each do |key,value| service_def = test_service.dup service_def.delete(key) expect { Chef::Application::WindowsServiceManager.new(service_def) }.to raise_error(ArgumentError) end end end context "with valid definition" do before(:each) do @service_manager_output = [ ] # Uncomment below lines to debug this test # original_puts = $stdout.method(:puts) allow($stdout).to receive(:puts) do |message| @service_manager_output << message # original_puts.call(message) end end after(:each) do cleanup end context "when service doesn't exist" do it "default => should say service don't exist" do service_manager.run expect(@service_manager_output.grep(/doesn't exist on the system/).length).to be > 0 end it "install => should install the service" do service_manager.run(["-a", "install"]) expect(test_service_exists?).to be_truthy end it "other actions => should say service doesn't exist" do ["delete", "start", "stop", "pause", "resume", "uninstall"].each do |action| service_manager.run(["-a", action]) expect(@service_manager_output.grep(/doesn't exist on the system/).length).to be > 0 @service_manager_output = [ ] end end end context "when service exists" do before(:each) do service_manager.run(["-a", "install"]) end it "should have an own-process, non-interactive type" do status = ::Win32::Service.status("spec-service") expect(status[:service_type]).to eq("own process") expect(status[:interactive]).to be_falsey end it "install => should say service already exists" do service_manager.run(["-a", "install"]) expect(@service_manager_output.grep(/already exists/).length).to be > 0 end context "and service is stopped" do ["delete", "uninstall"].each do |action| it "#{action} => should remove the service", :volatile do service_manager.run(["-a", action]) expect(test_service_exists?).to be_falsey end end it "default, status => should say service is stopped" do service_manager.run([ ]) expect(@service_manager_output.grep(/stopped/).length).to be > 0 @service_manager_output = [ ] service_manager.run(["-a", "status"]) expect(@service_manager_output.grep(/stopped/).length).to be > 0 end it "start should start the service", :volatile do service_manager.run(["-a", "start"]) expect(test_service_state).to eq("running") expect(File.exists?(test_service_file)).to be_truthy end it "stop should not affect the service" do service_manager.run(["-a", "stop"]) expect(test_service_state).to eq("stopped") end ["pause", "resume"].each do |action| it "#{action} => should raise error" do expect { service_manager.run(["-a", action]) }.to raise_error(SystemCallError) end end context "and service is started", :volatile do before(:each) do service_manager.run(["-a", "start"]) end ["delete", "uninstall"].each do |action| it "#{action} => should remove the service", :volatile do service_manager.run(["-a", action]) expect(test_service_exists?).to be_falsey end end it "default, status => should say service is running" do service_manager.run([ ]) expect(@service_manager_output.grep(/running/).length).to be > 0 @service_manager_output = [ ] service_manager.run(["-a", "status"]) expect(@service_manager_output.grep(/running/).length).to be > 0 end it "stop should stop the service" do service_manager.run(["-a", "stop"]) expect(test_service_state).to eq("stopped") end it "pause should pause the service" do service_manager.run(["-a", "pause"]) expect(test_service_state).to eq("paused") end it "resume should have no affect" do service_manager.run(["-a", "resume"]) expect(test_service_state).to eq("running") end end context "and service is paused", :volatile do before(:each) do service_manager.run(["-a", "start"]) service_manager.run(["-a", "pause"]) end actions = ["delete", "uninstall"] actions.each do |action| it "#{action} => should remove the service" do service_manager.run(["-a", action]) expect(test_service_exists?).to be_falsey end end it "default, status => should say service is paused" do service_manager.run([ ]) expect(@service_manager_output.grep(/paused/).length).to be > 0 @service_manager_output = [ ] service_manager.run(["-a", "status"]) expect(@service_manager_output.grep(/paused/).length).to be > 0 end it "stop should stop the service" do service_manager.run(["-a", "stop"]) expect(test_service_state).to eq("stopped") end it "pause should not affect the service" do service_manager.run(["-a", "pause"]) expect(test_service_state).to eq("paused") end it "start should raise an error" do expect {service_manager.run(["-a", "start"])}.to raise_error(::Win32::Service::Error) end end end end end end chef-12.3.0/spec/functional/file_content_management/0000755000004100000410000000000012520074675022503 5ustar www-datawww-datachef-12.3.0/spec/functional/file_content_management/deploy_strategies_spec.rb0000644000004100000410000001462312520074675027576 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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_examples_for "a content deploy strategy" do def normalize_mode(mode_int) ( mode_int & 07777).to_s(8) end let(:sandbox_dir) do basename = make_tmpname("content-deploy-tests") full_path = File.join(CHEF_SPEC_DATA, basename) FileUtils.mkdir_p(full_path) full_path end after do FileUtils.rm_rf(sandbox_dir) if File.exist?(sandbox_dir) end let(:content_deployer) { described_class.new } let(:target_file_path) { File.join(sandbox_dir, "cp-deploy-strategy-target-file.txt") } describe "creating the file" do ## # UNIX Context let(:default_mode) { normalize_mode(0666 & ~File.umask) } it "touches the file to create it (UNIX)", :unix_only do content_deployer.create(target_file_path) expect(File).to exist(target_file_path) file_info = File.stat(target_file_path) expect(file_info).to be_owned expect(file_info).to be_file expect(normalize_mode(file_info.mode)).to eq(default_mode) end ## # Window Context let(:parent_dir) { File.dirname(target_file_path) } let(:parent_security_descriptor) do security_obj = Chef::ReservedNames::Win32::Security::SecurableObject.new(parent_dir) security_obj.security_descriptor(true) end let(:masks) do Chef::ReservedNames::Win32::API::Security end def ace_inherits?(ace) flags = ace.flags (flags & masks::OBJECT_INHERIT_ACE) !=0 end let(:parent_inheritable_aces) do inheritable_aces = parent_security_descriptor.dacl.select do |ace| ace_inherits?(ace) end end it "touches the file to create it (Windows)", :windows_only do content_deployer.create(target_file_path) expect(File).to exist(target_file_path) file_info = File.stat(target_file_path) expect(file_info).to be_owned expect(file_info).to be_file parent_aces = parent_inheritable_aces security_obj = Chef::ReservedNames::Win32::Security::SecurableObject.new(target_file_path) security_descriptor = security_obj.security_descriptor(true) # On certain windows systems like 2003 and Azure VMs there are some default # ACEs that are not inherited from parents. So filter out the parents before # comparing the aces self_aces = security_descriptor.dacl.select do |ace| ace_inherits?(ace) end self_aces.each_with_index do |ace, index| expect(ace.mask).to eq(parent_aces[index].mask) end end end describe "updating the file" do let(:staging_dir) { Dir.mktmpdir } let(:staging_file_content) { "this is the expected content" } let(:staging_file_path) do path = File.join(staging_dir, "cp-deploy-strategy-staging-file.txt") File.open(path, "w+", 0600) { |f| f.print(staging_file_content) } path end def unix_invariant_properies(stat_struct) unix_invariants.inject({}) do |property_map, property| property_map[property] = stat_struct.send(property) property_map end end def win_invariant_properties(sec_obj) descriptor = sec_obj.security_descriptor(true) security_descriptor_invariants.inject({}) do |prop_map, property| prop_map[property] = descriptor.send(property) prop_map end end before do content_deployer.create(target_file_path) end it "maintains invariant properties on UNIX", :unix_only do original_info = File.stat(target_file_path) content_deployer.deploy(staging_file_path, target_file_path) updated_info = File.stat(target_file_path) expect(unix_invariant_properies(original_info)).to eq(unix_invariant_properies(updated_info)) end it "maintains invariant properties on Windows", :windows_only do original_info = Chef::ReservedNames::Win32::Security::SecurableObject.new(target_file_path) content_deployer.deploy(staging_file_path, target_file_path) updated_info = Chef::ReservedNames::Win32::Security::SecurableObject.new(target_file_path) expect(win_invariant_properties(original_info)).to eq(win_invariant_properties(updated_info)) end it "updates the target with content from staged" do content_deployer.deploy(staging_file_path, target_file_path) expect(IO.binread(target_file_path)).to eq(staging_file_content) end context "when the owner of the target file is not the owner of the staging file", :requires_root do before do File.chown(1337, 1337, target_file_path) end it "copies the staging file's content" do original_info = File.stat(target_file_path) content_deployer.deploy(staging_file_path, target_file_path) updated_info = File.stat(target_file_path) expect(unix_invariant_properies(original_info)).to eq(unix_invariant_properies(updated_info)) end end end end describe Chef::FileContentManagement::Deploy::Cp do let(:unix_invariants) do [ :uid, :gid, :mode, :ino ] end let(:security_descriptor_invariants) do [ :owner, :group, :dacl ] end it_should_behave_like "a content deploy strategy" end describe Chef::FileContentManagement::Deploy::MvUnix, :unix_only do let(:unix_invariants) do [ :uid, :gid, :mode ] end it_should_behave_like "a content deploy strategy" end # On Unix we won't have loaded the file, avoid undefined constant errors: class Chef::FileContentManagement::Deploy::MvWindows ; end describe Chef::FileContentManagement::Deploy::MvWindows, :windows_only do context "when a file has no sacl" do let(:security_descriptor_invariants) do [ :owner, :group, :dacl ] end it_should_behave_like "a content deploy strategy" end end chef-12.3.0/spec/functional/provider/0000755000004100000410000000000012520074675017470 5ustar www-datawww-datachef-12.3.0/spec/functional/provider/whyrun_safe_ruby_block_spec.rb0000644000004100000410000000272412520074675025601 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 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::WhyrunSafeRubyBlock do let(:node) { Chef::Node.new } let(:run_context) { events = Chef::EventDispatch::Dispatcher.new Chef::RunContext.new(node, {}, events) } before do $evil_global_evil_laugh = :wahwah Chef::Config[:why_run] = true end after do Chef::Config[:why_run] = false end describe "when testing the resource" do let(:new_resource) do r = Chef::Resource::WhyrunSafeRubyBlock.new("reload all", run_context) r.block { $evil_global_evil_laugh = :mwahahaha } r end it "updates the evil laugh, even in why-run mode" do new_resource.run_action(new_resource.action) expect($evil_global_evil_laugh).to eq(:mwahahaha) expect(new_resource).to be_updated end end end chef-12.3.0/spec/functional/provider/remote_file/0000755000004100000410000000000012520074675021762 5ustar www-datawww-datachef-12.3.0/spec/functional/provider/remote_file/cache_control_data_spec.rb0000755000004100000410000000660212520074675027124 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 'uri' describe Chef::Provider::RemoteFile::CacheControlData do before do @original_config = Chef::Config.hash_dup end after do Chef::Config.configuration = @original_config if @original_config end before(:each) do Chef::Config[:file_cache_path] = Dir.mktmpdir end after(:each) do FileUtils.rm_rf(Chef::Config[:file_cache_path]) end let(:uri) { URI.parse("http://www.bing.com/robots.txt") } describe "when the cache control data save method is invoked" do subject(:cache_control_data) do Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum) end # the checksum of the file last we fetched it. let(:file_checksum) { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } let(:etag) { "\"a-strong-identifier\"" } let(:mtime) { "Thu, 01 Aug 2013 08:16:32 GMT" } before do cache_control_data.etag = etag cache_control_data.mtime = mtime cache_control_data.checksum = file_checksum end it "writes data to the cache" do cache_control_data.save end it "writes the data to the cache and the same data can be read back" do cache_control_data.save saved_cache_control_data = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum) expect(saved_cache_control_data.etag).to eq(cache_control_data.etag) expect(saved_cache_control_data.mtime).to eq(cache_control_data.mtime) expect(saved_cache_control_data.checksum).to eq(cache_control_data.checksum) end # Cover the very long remote file path case -- see CHEF-4422 where # local cache file names generated from the long uri exceeded # local file system path limits resulting in exceptions from # file system API's on both Windows and Unix systems. context "when the length of the uri exceeds the path length limits for the local file system" do let(:uri_exceeds_file_system_limit) do URI.parse("http://www.bing.com/" + ('0' * 1024)) end let(:uri) { uri_exceeds_file_system_limit } it "writes data to the cache" do expect do cache_control_data.save end.not_to raise_error end it "writes the data to the cache and the same data can be read back" do cache_control_data.save saved_cache_control_data = Chef::Provider::RemoteFile::CacheControlData.load_and_validate(uri, file_checksum) expect(saved_cache_control_data.etag).to eq(cache_control_data.etag) expect(saved_cache_control_data.mtime).to eq(cache_control_data.mtime) expect(saved_cache_control_data.checksum).to eq(cache_control_data.checksum) end end end end chef-12.3.0/spec/functional/audit/0000755000004100000410000000000012520074675016744 5ustar www-datawww-datachef-12.3.0/spec/functional/audit/rspec_formatter_spec.rb0000644000004100000410000000342112520074675023502 0ustar www-datawww-data# # Author:: Tyler Ball () # Author:: Claire McQuin () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'rspec/core/sandbox' require 'chef/audit/runner' require 'rspec/support/spec/in_sub_process' require 'rspec/support/spec/stderr_splitter' require 'chef/audit/rspec_formatter' describe Chef::Audit::RspecFormatter do include RSpec::Support::InSubProcess let(:events) { double("events").as_null_object } let(:audits) { {} } let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) } let(:runner) { Chef::Audit::Runner.new(run_context) } let(:output) { double("output") } # aggressively define this so we can mock out the new call later let!(:formatter) { Chef::Audit::RspecFormatter.new(output) } around(:each) do |ex| RSpec::Core::Sandbox.sandboxed { ex.run } end it "should not close the output using our formatter" do in_sub_process do expect_any_instance_of(Chef::Audit::RspecFormatter).to receive(:new).and_return(formatter) expect(formatter).to receive(:close).and_call_original expect(output).to_not receive(:close) runner.run end end end chef-12.3.0/spec/functional/audit/runner_spec.rb0000644000004100000410000001031512520074675021614 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'rspec/core/sandbox' require 'chef/audit/runner' require 'rspec/support/spec/in_sub_process' require 'rspec/support/spec/stderr_splitter' require 'tempfile' ## # This functional test ensures that our runner can be setup to not interfere with existing RSpec # configuration and world objects. When normally running Chef, there is only 1 RSpec instance # so this isn't needed. In unit testing the Runner should be mocked appropriately. describe Chef::Audit::Runner do # The functional tests must be run in a sub_process. Including Serverspec includes the Serverspec DSL - this # conflicts with our `package` DSL (among others) when we try to test `package` inside an RSpec example. # Our DSL leverages `method_missing` while the Serverspec DSL defines a method on the RSpec::Core::ExampleGroup. # The defined method wins our and returns before our `method_missing` DSL can be called. # # Running in a sub_process means the serverspec libraries will only be included in a forked process, not the main one. include RSpec::Support::InSubProcess let(:events) { double("events").as_null_object } let(:runner) { Chef::Audit::Runner.new(run_context) } let(:stdout) { StringIO.new } around(:each) do |ex| RSpec::Core::Sandbox.sandboxed { ex.run } end before do Chef::Config[:log_location] = stdout end describe "#run" do let(:audits) { {} } let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) } let(:control_group_name) { "control_group_name" } it "Correctly runs an empty controls block" do in_sub_process do runner.run end end shared_context "passing audit" do let(:audits) do should_pass = lambda do it "should pass" do expect(2 - 2).to eq(0) end end { control_group_name => Struct.new(:args, :block).new([control_group_name], should_pass)} end end shared_context "failing audit" do let(:audits) do should_fail = lambda do it "should fail" do expect(2 - 1).to eq(0) end end { control_group_name => Struct.new(:args, :block).new([control_group_name], should_fail)} end end context "there is a single successful control" do include_context "passing audit" it "correctly runs" do in_sub_process do runner.run expect(stdout.string).to match(/1 example, 0 failures/) end end end context "there is a single failing control" do include_context "failing audit" it "correctly runs" do in_sub_process do runner.run expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/) expect(stdout.string).to match(/1 example, 1 failure/) expect(stdout.string).to match(/# control_group_name should fail/) end end end describe "log location is a file" do let(:tmpfile) { Tempfile.new("audit") } before do Chef::Config[:log_location] = tmpfile.path end after do tmpfile.close tmpfile.unlink end include_context "failing audit" it "correctly runs" do in_sub_process do runner.run contents = tmpfile.read expect(contents).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/) expect(contents).to match(/1 example, 1 failure/) expect(contents).to match(/# control_group_name should fail/) end end end end end chef-12.3.0/spec/functional/assets/0000755000004100000410000000000012520074675017140 5ustar www-datawww-datachef-12.3.0/spec/functional/assets/testchefsubsys0000755000004100000410000000024012520074675022140 0ustar www-datawww-data#!/bin/bash # trapchild sleep 120 & pid="$!" trap 'echo I am going down, so killing off my processes..; kill $pid; exit' SIGHUP SIGINT SIGQUIT SIGTERM waitchef-12.3.0/spec/functional/assets/chefinittest0000755000004100000410000000123712520074675021562 0ustar www-datawww-data#!/bin/ksh function create_chef_txt { touch /tmp/chefinittest.txt } function delete_chef_txt { rm /tmp/chefinittest.txt } function rename_chef_txt { mv /tmp/chefinittest.txt /tmp/$1 } case "$1" in start ) create_chef_txt ;; stop ) delete_chef_txt ;; status ) [ -f /tmp/chefinittest.txt ] || [ -f /tmp/chefinittest_reload.txt ] || [ -f /tmp/chefinittest_restart.txt ] ;; reload ) rename_chef_txt "chefinittest_reload.txt" ;; restart ) rename_chef_txt "chefinittest_restart.txt" ;; * ) echo "Usage: $0 (start | stop | restart | reload)" exit 1 esac chef-12.3.0/spec/functional/assets/dummy-2-0.aix6.1.noarch.rpm0000644000004100000410000000171412520074675023570 0ustar www-datawww-datadummy-2-0$ݽ6`}4Dd   48LPes (89:b&d8e=fBCdummy20dummy packageThis is a dummy package.QLlpar10ml16ed_pubDummies IncorporatedDummy LicenseLuciano Rocha Networking/Daemonshttp://www.dontbeadummy.org/aix6.1noarchdummy-2-0.src.rpm|3.0.5C Luciano Rocha 0- dummy-O2 -fsigned-charcpiogzip93070704 $!A>A @5|chef-12.3.0/spec/functional/assets/dummy-1-0.aix6.1.noarch.rpm0000644000004100000410000000171412520074675023567 0ustar www-datawww-datadummy-1-0$TOZ;X0gp#/Dd   48LPes (89:b&d8e=fBCdummy10dummy packageThis is a dummy package.Qlpar10ml16ed_pubDummies IncorporatedDummy LicenseLuciano Rocha Networking/Daemonshttp://www.dontbeadummy.org/aix6.1noarchdummy-1-0.src.rpm|3.0.5C Luciano Rocha 0- dummy-O2 -fsigned-charcpiogzip93070704 $!A>A @5|chef-12.3.0/spec/functional/assets/mytest-2.0-1.noarch.rpm0000644000004100000410000000414512520074675023115 0ustar www-datawww-datamytest-2.0-1T>D ,0@d1354e1297e641203ccab95d9556699a84e59639Mm`-c[3>5V?Fd    HLpt x    2 4<FPhpx(>GHIXY\]^!b4defkltuvwx{Cmytest2.01A test ScriptA test script inside a simple RPM packageQkaustubh-centos5-64-cron.opscode.usMITUtilitieshttp://www.oracle-base.com/articles/linux/linux-build-simple-rpm-packages.phplinuxnoarchchmod 755 -R /opt/mytestAQQfc457c3017d6f95b0876c7ebb6f58fcdrootrootrootrootmytest-2.0-1.src.rpmmytest@JJ/bin/shrpmlib(CompressedFileNames)rpmlib(PayloadFilesHavePrefix)3.0.4-14.0-14.4.2.3/bin/shldle2.0-1mytestmytest.sh/opt//opt/mytest/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=genericcpiogzip9noarchnoarch-redhat-linux-gnuASCII textdirectorysystem_u:object_r:usr_t:s0system_u:object_r:usr_t:s0?30707040004K63&)؁aZApZ~7OO?D?$nSn Q6L&nCslvC)  HMWP ,Vļ"%.ĪCI0FHk"].X@chef-12.3.0/spec/functional/assets/PkgA.1.0.0.0.bff0000644000004100000410000000700012520074675021227 0ustar www-datawww-data kSQSQby nameby namerootd kyASQSQSQ ./n/A kySQSQSQ ./lpp_nameem4 R I PkgA { PkgA.rte 1.0.0.0 01 N U en_US My runtime fileset [ % /usr/PkgA 0 /usr/PkgA/bin 8 /usr/doc/PkgA 8 /usr/lib/objrepos 5 INSTWORK 16 16 % % % % % ] } kRMARQSQSQ ./usraA k$QASQSQSQ ./usr/lpppA k^ASQSQSQ ./usr/lpp/PkgA/pA k`2\SQSQSQ \./usr/lpp/PkgA/liblpp.a/p 1444 0 68 1258 0 87 260 0 1375425475 0 0 644 11 PkgA.rte.al` ./usr/doc/PkgA ./usr/doc/PkgA/README ./usr/PkgA ./usr/PkgA/bin ./usr/PkgA/bin/acommand 887 1258 68 1375425475 0 0 644 18 PkgA.rte.inventory` /usr/doc/PkgA: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/doc/PkgA/README: owner = root group = system mode = 644 type = FILE class = apply,inventory,PkgA.rte size = 24 checksum = "08645 1 " /usr/PkgA: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/PkgA/bin: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/PkgA/bin/acommand: owner = root group = system mode = 744 type = FILE class = apply,inventory,PkgA.rte size = 42 checksum = "23244 1 " 79 1444 260 1375425475 0 0 644 13 PkgA.rte.size` /usr/PkgA 0 /usr/PkgA/bin 8 /usr/doc/PkgA 8 /usr/lib/objrepos 5 INSTWORK 16 16 93 0 1258 0 0 0 0 0 ` 3 68 260 1258 PkgA.rte.alPkgA.rte.inventoryPkgA.rte.size kAfSQRQRQ ./usr/doc/PkgA/cA kSQnSQnSQ ./usr/doc/PkgA/README/cThis is the PkgA README kꨣA4SQ8SQ8SQ ./usr/PkgAgA/RAgA k꾇A8SQASQASQ ./usr/PkgA/binAgA k꬚*SQUSQYSQ *./usr/PkgA/bin/acommandAg#!/bin/sh echo "Here is my PkgA command" 1258k ./usr/doc/PkgA/README/cThis is the PkgA README kꨣA4SQ8SQ8SQ ./usr/PkgAgA/RAgA k꾇A8SQASQASQ ./usr/PkgA/binAgchef-12.3.0/spec/functional/assets/PkgA.2.0.0.0.bff0000644000004100000410000000700012520074675021230 0ustar www-datawww-data k8kQkQby nameby namerootd k AkQkQkQ ./n/A kkQkQkQ ./lpp_nameem4 R I PkgA { PkgA.rte 2.0.0.0 01 N U en_US My runtime fileset [ % /usr/PkgA 0 /usr/PkgA/bin 8 /usr/doc/PkgA 8 /usr/lib/objrepos 5 INSTWORK 16 16 % % % % % ] } k@yAkQSQSQ ./usraA k$QASQSQSQ ./usr/lpppA kꮄASQkQkQ ./usr/lpp/PkgA/pA kؠ\kQkQkQ \./usr/lpp/PkgA/liblpp.a/p 1444 0 68 1258 0 87 260 0 1375431679 0 0 644 11 PkgA.rte.al` ./usr/doc/PkgA ./usr/doc/PkgA/README ./usr/PkgA ./usr/PkgA/bin ./usr/PkgA/bin/acommand 887 1258 68 1375431679 0 0 644 18 PkgA.rte.inventory` /usr/doc/PkgA: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/doc/PkgA/README: owner = root group = system mode = 644 type = FILE class = apply,inventory,PkgA.rte size = 24 checksum = "08645 1 " /usr/PkgA: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/PkgA/bin: owner = root group = system mode = 755 type = DIRECTORY class = apply,inventory,PkgA.rte /usr/PkgA/bin/acommand: owner = root group = system mode = 744 type = FILE class = apply,inventory,PkgA.rte size = 46 checksum = "62939 1 " 79 1444 260 1375431679 0 0 644 13 PkgA.rte.size` /usr/PkgA 0 /usr/PkgA/bin 8 /usr/doc/PkgA 8 /usr/lib/objrepos 5 INSTWORK 16 16 93 0 1258 0 0 0 0 0 ` 3 68 260 1258 PkgA.rte.alPkgA.rte.inventoryPkgA.rte.size kAfSQRQRQ ./usr/doc/PkgA/cA k.kQnSQnSQ ./usr/doc/PkgA/README/cThis is the PkgA README kꨢAkQ8SQ8SQ ./usr/PkgAgA/RAgA kꨉAkQASQASQ ./usr/PkgA/binAgA k&.kQkQkQ ../usr/PkgA/bin/acommandAg#!/bin/sh echo "Here is my PkgA command 2.0" 58k ./usr/doc/PkgA/README/cThis is the PkgA README kꨢAkQ8SQ8SQ ./usr/PkgAgA/RAgA kꨉAkQASQASQ ./usr/PkgA/binAgchef-12.3.0/spec/functional/assets/mytest-1.0-1.noarch.rpm0000644000004100000410000000411612520074675023112 0ustar www-datawww-datamytest-1.0-1T>D ,0@f613507b95c6c5a51d7aec52373784dd2b49f4916sS &k>5F?6d    HL`d hr    " $,6@X`ht(>GHIXY\]^b$defkltuvwx{Cmytest1.01A test scriptA test script inside a simple RPM packageQbPrabhu1.localdomainMITUtilitieshttp://www.oracle-base.com/articles/linux/linux-build-simple-rpm-packages.phplinuxnoarchchmod 755 -R /opt/mytestAQbQb4cdc303079a9a328fb74f7dfc01e2008rootrootrootrootmytest-1.0-1.src.rpmmytest@JJ/bin/shrpmlib(CompressedFileNames)rpmlib(PayloadFilesHavePrefix)3.0.4-14.0-14.4.2.3/bin/sh''1.0-1mytestmytest.sh/opt//opt/mytest/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=genericcpiogzip9noarchnoarch-redhat-linux-gnuASCII textdirectorysystem_u:object_r:usr_t:s0system_u:object_r:usr_t:s0?307070400L120102 U8 zV00 m m!݆fmhn(W J! @RB(@:tː GO EEE(echef-12.3.0/spec/functional/tiny_server_spec.rb0000644000004100000410000000534712520074675021557 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'tiny_server' describe TinyServer::API do before do @api = TinyServer::API.instance @api.clear end it "is a Singleton" do expect {TinyServer::API.new}.to raise_error end it "clears the router" do @api.get('/blargh', 200, "blargh") @api.clear expect(@api.routes["GET"]).to be_empty end it "creates a route for a GET request" do @api.get('/foo/bar', 200, 'hello foobar') # WEBrick gives you the full URI with host, Thin only gave the part after scheme+host+port response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => 'http://localhost:1974/foo/bar') expect(response).to eq([200, {'Content-Type' => 'application/json'}, [ 'hello foobar' ]]) end it "creates a route for a request with a block" do block_called = false @api.get('/bar/baz', 200) { block_called = true; 'hello barbaz' } response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => 'http://localhost:1974/bar/baz') expect(response).to eq([200, {'Content-Type' => 'application/json'}, [ 'hello barbaz' ]]) expect(block_called).to be_truthy end it "returns debugging info for 404s" do response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => '/no_such_thing') expect(response[0]).to eq(404) expect(response[1]).to eq({'Content-Type' => 'application/json'}) expect(response[2]).to be_a_kind_of(Array) response_obj = Chef::JSONCompat.from_json(response[2].first) expect(response_obj["message"]).to eq("no data matches the request for /no_such_thing") expect(response_obj["available_routes"]).to eq({"GET"=>[], "PUT"=>[], "POST"=>[], "DELETE"=>[]}) expect(response_obj["request"]).to eq({"REQUEST_METHOD"=>"GET", "REQUEST_URI"=>"/no_such_thing"}) end end describe TinyServer::Manager do it "runs the server" do @server = TinyServer::Manager.new @server.start TinyServer::API.instance.get("/index", 200, "[\"hello\"]") rest = Chef::REST.new('http://localhost:9000', false, false) expect(rest.get_rest("index")).to eq(["hello"]) @server.stop end end chef-12.3.0/spec/functional/knife/0000755000004100000410000000000012520074675016732 5ustar www-datawww-datachef-12.3.0/spec/functional/knife/ssh_spec.rb0000644000004100000410000001741312520074675021074 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'tiny_server' describe Chef::Knife::Ssh do before(:all) do Chef::Knife::Ssh.load_deps @server = TinyServer::Manager.new @server.start end after(:all) do @server.stop end describe "identity file" do context "when knife[:ssh_identity_file] is set" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/aws.rsa" end it "uses the ssh_identity_file" do @knife.run expect(@knife.config[:identity_file]).to eq("~/.ssh/aws.rsa") end end context "when knife[:ssh_identity_file] is set and frozen" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/aws.rsa".freeze end it "uses the ssh_identity_file" do @knife.run expect(@knife.config[:identity_file]).to eq("~/.ssh/aws.rsa") end end context "when -i is provided" do before do setup_knife(['-i ~/.ssh/aws.rsa', '*:*', 'uptime']) Chef::Config[:knife][:ssh_identity_file] = nil end it "should use the value on the command line" do @knife.run expect(@knife.config[:identity_file]).to eq("~/.ssh/aws.rsa") end it "should override what is set in knife.rb" do Chef::Config[:knife][:ssh_identity_file] = "~/.ssh/other.rsa" @knife.run expect(@knife.config[:identity_file]).to eq("~/.ssh/aws.rsa") end end context "when knife[:ssh_identity_file] is not provided]" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_identity_file] = nil end it "uses the default" do @knife.run expect(@knife.config[:identity_file]).to eq(nil) end end end describe "port" do context "when -p 31337 is provided" do before do setup_knife(['-p 31337', '*:*', 'uptime']) end it "uses the ssh_port" do @knife.run expect(@knife.config[:ssh_port]).to eq("31337") end end end describe "user" do context "when knife[:ssh_user] is set" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_user] = "ubuntu" end it "uses the ssh_user" do @knife.run expect(@knife.config[:ssh_user]).to eq("ubuntu") end end context "when knife[:ssh_user] is set and frozen" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_user] = "ubuntu".freeze end it "uses the ssh_user" do @knife.run expect(@knife.config[:ssh_user]).to eq("ubuntu") end end context "when -x is provided" do before do setup_knife(['-x ubuntu', '*:*', 'uptime']) Chef::Config[:knife][:ssh_user] = nil end it "should use the value on the command line" do @knife.run expect(@knife.config[:ssh_user]).to eq("ubuntu") end it "should override what is set in knife.rb" do Chef::Config[:knife][:ssh_user] = "root" @knife.run expect(@knife.config[:ssh_user]).to eq("ubuntu") end end context "when knife[:ssh_user] is not provided]" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_user] = nil end it "uses the default (current user)" do @knife.run expect(@knife.config[:ssh_user]).to eq(nil) end end end describe "attribute" do context "when knife[:ssh_attribute] is set" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_attribute] = "ec2.public_hostname" end it "uses the ssh_attribute" do @knife.run expect(@knife.config[:attribute]).to eq("ec2.public_hostname") end end context "when knife[:ssh_attribute] is not provided]" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_attribute] = nil end it "uses the default" do @knife.run expect(@knife.config[:attribute]).to eq("fqdn") end end context "when -a ec2.public_ipv4 is provided" do before do setup_knife(['-a ec2.public_hostname', '*:*', 'uptime']) Chef::Config[:knife][:ssh_attribute] = nil end it "should use the value on the command line" do @knife.run expect(@knife.config[:attribute]).to eq("ec2.public_hostname") end it "should override what is set in knife.rb" do # This is the setting imported from knife.rb Chef::Config[:knife][:ssh_attribute] = "fqdn" # Then we run knife with the -a flag, which sets the above variable setup_knife(['-a ec2.public_hostname', '*:*', 'uptime']) @knife.run expect(@knife.config[:attribute]).to eq("ec2.public_hostname") end end end describe "gateway" do context "when knife[:ssh_gateway] is set" do before do setup_knife(['*:*', 'uptime']) Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname" end it "uses the ssh_gateway" do expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", {}) @knife.run expect(@knife.config[:ssh_gateway]).to eq("user@ec2.public_hostname") end end context "when -G user@ec2.public_hostname is provided" do before do setup_knife(['-G user@ec2.public_hostname', '*:*', 'uptime']) Chef::Config[:knife][:ssh_gateway] = nil end it "uses the ssh_gateway" do expect(@knife.session).to receive(:via).with("ec2.public_hostname", "user", {}) @knife.run expect(@knife.config[:ssh_gateway]).to eq("user@ec2.public_hostname") end end context "when the gateway requires a password" do before do setup_knife(['-G user@ec2.public_hostname', '*:*', 'uptime']) Chef::Config[:knife][:ssh_gateway] = nil allow(@knife.session).to receive(:via) do |host, user, options| raise Net::SSH::AuthenticationFailed unless options[:password] end end it "should prompt the user for a password" do expect(@knife.ui).to receive(:ask).with("Enter the password for user@ec2.public_hostname: ").and_return("password") @knife.run end end end def setup_knife(params=[]) @knife = Chef::Knife::Ssh.new(params) # We explicitly avoid running #configure_chef, which would read a knife.rb # if available, but #merge_configs (which is called by #configure_chef) is # necessary to have default options merged in. @knife.merge_configs allow(@knife).to receive(:ssh_command) { 0 } @api = TinyServer::API.instance @api.clear Chef::Config[:node_name] = nil Chef::Config[:client_key] = nil Chef::Config[:chef_server_url] = 'http://localhost:9000' @api.get("/search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0", 200) { %({"total":1, "start":0, "rows":[{"name":"i-xxxxxxxx", "json_class":"Chef::Node", "automatic":{"fqdn":"the.fqdn", "ec2":{"public_hostname":"the_public_hostname"}},"recipes":[]}]}) } end end chef-12.3.0/spec/functional/knife/smoke_test.rb0000644000004100000410000000234012520074675021433 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 "knife smoke tests" do # Since our specs load all code, there could be a case where knife does not # run correctly b/c of a missing require, but is not caught by other tests. # # We run `knife -v` to verify that knife at least loads all its code. it "can run and print its version" do knife_path = File.expand_path("../../bin/knife", CHEF_SPEC_DATA) knife_cmd = Mixlib::ShellOut.new("#{knife_path} -v") knife_cmd.run_command knife_cmd.error! expect(knife_cmd.stdout).to include(Chef::VERSION) end end chef-12.3.0/spec/functional/knife/cookbook_delete_spec.rb0000644000004100000410000001333212520074675023423 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'tiny_server' describe Chef::Knife::CookbookDelete do before(:all) do @server = TinyServer::Manager.new @server.start end before(:each) do @knife = Chef::Knife::CookbookDelete.new @api = TinyServer::API.instance @api.clear Chef::Config[:node_name] = nil Chef::Config[:client_key] = nil Chef::Config[:chef_server_url] = 'http://localhost:9000' end after(:all) do @server.stop end context "when the cookbook doesn't exist" do before do @log_output = StringIO.new Chef::Log.logger = Logger.new(@log_output) Chef::Log.level = :debug @knife.name_args = %w{no-such-cookbook} @api.get("/cookbooks/no-such-cookbook", 404, Chef::JSONCompat.to_json({'error'=>'dear Tim, no. -Sent from my iPad'})) end it "logs an error and exits" do allow(@knife.ui).to receive(:stderr).and_return(@log_output) expect {@knife.run}.to raise_error(SystemExit) expect(@log_output.string).to match(/Cannot find a cookbook named no-such-cookbook to delete/) end end context "when there is only one version of a cookbook" do before do @knife.name_args = %w{obsolete-cookbook} @cookbook_list = {'obsolete-cookbook' => { 'versions' => ['version' => '1.0.0']} } @api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list)) end it "asks for confirmation, then deletes the cookbook" do stdin, stdout = StringIO.new("y\n"), StringIO.new allow(@knife.ui).to receive(:stdin).and_return(stdin) allow(@knife.ui).to receive(:stdout).and_return(stdout) cb100_deleted = false @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" } @knife.run expect(stdout.string).to match(/#{Regexp.escape('Do you really want to delete obsolete-cookbook version 1.0.0? (Y/N)')}/) expect(cb100_deleted).to be_truthy end it "asks for confirmation before purging" do @knife.config[:purge] = true stdin, stdout = StringIO.new("y\ny\n"), StringIO.new allow(@knife.ui).to receive(:stdin).and_return(stdin) allow(@knife.ui).to receive(:stdout).and_return(stdout) cb100_deleted = false @api.delete("/cookbooks/obsolete-cookbook/1.0.0?purge=true", 200) { cb100_deleted = true; "[\"true\"]" } @knife.run expect(stdout.string).to match(/#{Regexp.escape('Are you sure you want to purge files')}/) expect(stdout.string).to match(/#{Regexp.escape('Do you really want to delete obsolete-cookbook version 1.0.0? (Y/N)')}/) expect(cb100_deleted).to be_truthy end end context "when there are several versions of a cookbook" do before do @knife.name_args = %w{obsolete-cookbook} versions = ['1.0.0', '1.1.0', '1.2.0'] with_version = lambda { |version| { 'version' => version } } @cookbook_list = {'obsolete-cookbook' => { 'versions' => versions.map(&with_version) } } @api.get("/cookbooks/obsolete-cookbook", 200, Chef::JSONCompat.to_json(@cookbook_list)) end it "deletes all versions of a cookbook when given the '-a' flag" do @knife.config[:all] = true @knife.config[:yes] = true cb100_deleted = cb110_deleted = cb120_deleted = nil @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" } @api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" } @api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" } @knife.run expect(cb100_deleted).to be_truthy expect(cb110_deleted).to be_truthy expect(cb120_deleted).to be_truthy end it "asks which version to delete and deletes that when not given the -a flag" do cb100_deleted = cb110_deleted = cb120_deleted = nil @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" } stdin, stdout = StringIO.new, StringIO.new allow(@knife.ui).to receive(:stdin).and_return(stdin) allow(@knife.ui).to receive(:stdout).and_return(stdout) stdin << "1\n" stdin.rewind @knife.run expect(cb100_deleted).to be_truthy expect(stdout.string).to match(/Which version\(s\) do you want to delete\?/) end it "deletes all versions of the cookbook when not given the -a flag and the user chooses to delete all" do cb100_deleted = cb110_deleted = cb120_deleted = nil @api.delete("/cookbooks/obsolete-cookbook/1.0.0", 200) { cb100_deleted = true; "[\"true\"]" } @api.delete("/cookbooks/obsolete-cookbook/1.1.0", 200) { cb110_deleted = true; "[\"true\"]" } @api.delete("/cookbooks/obsolete-cookbook/1.2.0", 200) { cb120_deleted = true; "[\"true\"]" } stdin, stdout = StringIO.new("4\n"), StringIO.new allow(@knife.ui).to receive(:stdin).and_return(stdin) allow(@knife.ui).to receive(:stdout).and_return(stdout) @knife.run expect(cb100_deleted).to be_truthy expect(cb110_deleted).to be_truthy expect(cb120_deleted).to be_truthy end end end chef-12.3.0/spec/functional/knife/exec_spec.rb0000644000004100000410000000306312520074675021217 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'tiny_server' describe Chef::Knife::Exec do before(:all) do @server = TinyServer::Manager.new#(:debug => true) @server.start end before(:each) do @knife = Chef::Knife::Exec.new @api = TinyServer::API.instance @api.clear Chef::Config[:node_name] = nil Chef::Config[:client_key] = nil Chef::Config[:chef_server_url] = 'http://localhost:9000' $output = StringIO.new end after(:all) do @server.stop end it "executes a script in the context of the chef-shell main context" do @node = Chef::Node.new @node.name("ohai-world") response = {"rows" => [@node],"start" => 0,"total" => 1} @api.get(%r{^/search/node}, 200, Chef::JSONCompat.to_json(response)) code = "$output.puts nodes.all" @knife.config[:exec] = code @knife.run expect($output.string).to match(%r{node\[ohai-world\]}) end end chef-12.3.0/spec/functional/knife/configure_spec.rb0000644000004100000410000000222612520074675022254 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/knife/configure' require 'ohai' describe "knife configure" do let (:ohai) do o = Ohai::System.new o.load_plugins o.require_plugin 'os' o.require_plugin 'hostname' o end it "loads the fqdn from Ohai" do knife_configure = Chef::Knife::Configure.new hostname_guess = ohai[:fqdn] || ohai[:machinename] || ohai[:hostname] || 'localhost' expect(knife_configure.guess_servername).to eql(hostname_guess) end end chef-12.3.0/spec/functional/dsl/0000755000004100000410000000000012520074675016420 5ustar www-datawww-datachef-12.3.0/spec/functional/dsl/reboot_pending_spec.rb0000644000004100000410000001151612520074675022761 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require "chef/dsl/reboot_pending" require "chef/win32/registry" require "spec_helper" describe Chef::DSL::RebootPending, :windows_only do def run_ohai ohai = Ohai::System.new # Would be nice to limit this to platform/kernel/arch etc for Ohai 7 ohai.all_plugins node.consume_external_attrs(ohai.data,{}) ohai end def registry_unsafe? registry.value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }) || registry.key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') registry.key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired') || registry.key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') end let(:node) { Chef::Node.new } let(:events) { Chef::EventDispatch::Dispatcher.new } let!(:ohai) { run_ohai } # Ensure we have necessary node data let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:recipe) { Chef::Recipe.new("a windows cookbook", "the windows recipe", run_context) } let(:registry) { Chef::Win32::Registry.new(run_context) } describe "reboot_pending?" do describe "when there is nothing to indicate a reboot is pending" do it "should return false" do skip "Found existing registry keys" if registry_unsafe? expect(recipe.reboot_pending?).to be_falsey end end describe 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations' do it "returns true if the registry value exists" do skip "Found existing registry keys" if registry_unsafe? registry.set_value('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations', :type => :multi_string, :data => ['\??\C:\foo.txt|\??\C:\bar.txt'] }) expect(recipe.reboot_pending?).to be_truthy end after do unless registry_unsafe? registry.delete_value('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }) end end end describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' do it "returns true if the registry key exists" do skip "Found existing registry keys" if registry_unsafe? registry.create_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired', false) expect(recipe.reboot_pending?).to be_truthy end after do unless registry_unsafe? registry.delete_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired', false) end end end describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' do it "returns true if the registry key exists" do pending "Permissions are limited to 'TrustedInstaller' by default" skip "Found existing registry keys" if registry_unsafe? registry.create_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired', false) expect(recipe.reboot_pending?).to be_truthy end after do unless registry_unsafe? registry.delete_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired', false) end end end describe 'HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile\Flags' do it "returns true if the registry key exists" do skip "Found existing registry keys" if registry_unsafe? registry.create_key('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', true) registry.set_value('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', { :name => 'Flags', :type => :dword, :data => 3 }) expect(recipe.reboot_pending?).to be_truthy end after do unless registry_unsafe? registry.delete_value('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', { :name => 'Flags' }) registry.delete_key('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', false) end end end end end chef-12.3.0/spec/functional/dsl/registry_helper_spec.rb0000644000004100000410000000514312520074675023171 0ustar www-datawww-data# # Author:: Prajakta Purohit () # 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 "chef/dsl/registry_helper" require "spec_helper" describe Chef::Resource::RegistryKey, :windows_only do before (:all) do ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root" ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch" ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg| reg['RootType1', Win32::Registry::REG_SZ] = 'fibrous' reg.write('Roots', Win32::Registry::REG_MULTI_SZ, ["strong roots", "healthy tree"]) end events = Chef::EventDispatch::Dispatcher.new node = Chef::Node.new ohai = Ohai::System.new ohai.all_plugins node.consume_external_attrs(ohai.data,{}) run_context = Chef::RunContext.new(node, {}, events) @resource = Chef::Resource.new("foo", run_context) end context "tests registry dsl" do it "returns true if registry_key_exists" do expect(@resource.registry_key_exists?("HKCU\\Software\\Root")).to eq(true) end it "returns true if registry has specified value" do values = @resource.registry_get_values("HKCU\\Software\\Root") expect(values.include?({:name=>"RootType1",:type=>:string,:data=>"fibrous"})).to eq(true) end it "returns true if specified registry_has_subkey" do expect(@resource.registry_has_subkeys?("HKCU\\Software\\Root")).to eq(true) end it "returns true if specified key has specified subkey" do subkeys = @resource.registry_get_subkeys("HKCU\\Software\\Root") expect(subkeys.include?("Branch")).to eq(true) end it "returns true if registry_value_exists" do expect(@resource.registry_value_exists?("HKCU\\Software\\Root", {:name=>"RootType1", :type=>:string, :data=>"fibrous"})).to eq(true) end it "returns true if data_value_exists" do expect(@resource.registry_data_exists?("HKCU\\Software\\Root", {:name=>"RootType1", :type=>:string, :data=>"fibrous"})).to eq(true) end end end chef-12.3.0/spec/functional/application_spec.rb0000644000004100000410000000304512520074675021502 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'chef/mixin/shell_out' describe Chef::Application do include Chef::Mixin::ShellOut before do @original_argv = ARGV.dup ARGV.clear @original_env = ENV.to_hash ENV.clear @app = Chef::Application.new end after do ARGV.replace(@original_argv) ENV.clear ENV.update(@original_env) end describe "when proxy options are set in config" do before do Chef::Config[:http_proxy] = "http://proxy.example.org:8080" Chef::Config[:https_proxy] = nil Chef::Config[:ftp_proxy] = nil Chef::Config[:no_proxy] = nil @app.configure_proxy_environment_variables end it "saves built proxy to ENV which shell_out can use" do so = if windows? shell_out("echo %http_proxy%") else shell_out("echo $http_proxy") end expect(so.stdout.chomp).to eq("http://proxy.example.org:8080") end end end chef-12.3.0/spec/functional/run_lock_spec.rb0000644000004100000410000001674212520074675021023 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 File.expand_path('../../spec_helper', __FILE__) require 'chef/client' describe Chef::RunLock do # This behavior works on windows, but the tests use fork :( 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)}" end 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(lockfile) } it "creates the full path to the lockfile" do expect { run_lock.acquire }.not_to raise_error expect(File).to exist(lockfile) end it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do run_lock.acquire expect(run_lock.runlock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC) end it "allows only one chef client run per lockfile" do # First process, gets the lock and keeps it. p1 = fork do run_lock.acquire 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 record "p1 releasing lock" run_lock.release exit!(0) end # 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 record "p2 has lock" run_lock.release exit!(0) end Process.waitpid2(p1) Process.waitpid2(p2) raise_side_channel_error! expected=<<-E p1 has lock p1 releasing lock p2 has lock E expect(results).to eq(expected) end it "clears the lock if the process dies unexpectedly" do p1 = fork do run_lock.acquire 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 record "p2 has lock" run_lock.release exit! 0 end Process.waitpid2(p2) expect(results).to match(/p2 has lock\Z/) end it "test returns true and acquires the lock" do p1 = fork do expect(run_lock.test).to eq(true) sleep 2 exit! 1 end wait_on_lock p2 = fork do expect(run_lock.test).to eq(false) exit! 0 end Process.waitpid2(p2) Process.waitpid2(p1) end it "test returns without waiting when the lock is acquired" do p1 = fork do run_lock.acquire sleep 2 exit! 1 end wait_on_lock expect(run_lock.test).to eq(false) Process.waitpid2(p1) end it "doesn't truncate the lock file so that contents can be read" do p1 = fork do run_lock.acquire run_lock.save_pid sleep 2 exit! 1 end wait_on_lock sleep 0.5 # Possible race condition on Solaris which pid is observed as 0 expect(File.read(lockfile)).to eq(p1.to_s) Process.waitpid2(p1) end end end chef-12.3.0/spec/functional/http/0000755000004100000410000000000012520074675016615 5ustar www-datawww-datachef-12.3.0/spec/functional/http/simple_spec.rb0000644000004100000410000001307412520074675021452 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'tiny_server' require 'support/shared/functional/http' describe Chef::HTTP::Simple do include ChefHTTPShared let(:http_client) { described_class.new(source) } let(:http_client_disable_gzip) { described_class.new(source, { :disable_gzip => true } ) } before(:all) do start_tiny_server end after(:all) do stop_tiny_server end shared_examples_for "downloads requests correctly" do it "successfully downloads a streaming request" do tempfile = http_client.streaming_request(source, {}) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) end it "successfully does a non-streaming GET request" do expect(Digest::MD5.hexdigest(http_client.get(source))).to eq(Digest::MD5.hexdigest(expected_content)) end end shared_examples_for "validates content length and throws an exception" do it "successfully downloads a streaming request" do expect { http_client.streaming_request(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end it "successfully does a non-streaming GET request" do expect { http_client.get(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) end end shared_examples_for "an endpoint that 403s" do it "fails with a Net::HTTPServerException for a streaming request" do expect { http_client.streaming_request(source) }.to raise_error(Net::HTTPServerException) end it "fails with a Net::HTTPServerException for a GET request" do expect { http_client.get(source) }.to raise_error(Net::HTTPServerException) end end # see CHEF-5100 shared_examples_for "a 403 after a successful request when reusing the request object" do it "fails with a Net::HTTPServerException for a streaming request" do tempfile = http_client.streaming_request(source) tempfile.close expect(Digest::MD5.hexdigest(binread(tempfile.path))).to eq(Digest::MD5.hexdigest(expected_content)) expect { http_client.streaming_request(source2) }.to raise_error(Net::HTTPServerException) end it "fails with a Net::HTTPServerException for a GET request" do expect(Digest::MD5.hexdigest(http_client.get(source))).to eq(Digest::MD5.hexdigest(expected_content)) expect { http_client.get(source2) }.to raise_error(Net::HTTPServerException) end end it_behaves_like "downloading all the things" context "when Chef::Log.level = :debug" do before do Chef::Log.level = :debug @debug_log = '' allow(Chef::Log).to receive(:debug) { |str| @debug_log << str } end let(:source) { 'http://localhost:9000' } it "Logs the request and response for 200's but not the body" do http_client.get('http://localhost:9000/nyan_cat.png') expect(@debug_log).to match(/200/) expect(@debug_log).to match(/HTTP Request Header Data/) expect(@debug_log).to match(/HTTP Status and Header Data/) expect(@debug_log).not_to match(/HTTP Request Body/) expect(@debug_log).not_to match(/HTTP Response Body/) expect(@debug_log).not_to match(/Your request is just terrible./) end it "Logs the request and response for 200 POST, but not the body" do http_client.post('http://localhost:9000/posty', 'hithere') expect(@debug_log).to match(/200/) expect(@debug_log).to match(/HTTP Request Header Data/) expect(@debug_log).to match(/HTTP Status and Header Data/) expect(@debug_log).not_to match(/HTTP Request Body/) expect(@debug_log).not_to match(/hithere/) expect(@debug_log).not_to match(/HTTP Response Body/) expect(@debug_log).not_to match(/Your request is just terrible./) end it "Logs the request and response and bodies for 400 response" do expect do http_client.get('http://localhost:9000/bad_request') end.to raise_error(Net::HTTPServerException) expect(@debug_log).to match(/400/) expect(@debug_log).to match(/HTTP Request Header Data/) expect(@debug_log).to match(/HTTP Status and Header Data/) expect(@debug_log).not_to match(/HTTP Request Body/) expect(@debug_log).not_to match(/hithere/) expect(@debug_log).to match(/HTTP Response Body/) expect(@debug_log).to match(/Your request is just terrible./) end it "Logs the request and response and bodies for 400 POST response" do expect do http_client.post('http://localhost:9000/bad_request', 'hithere') end.to raise_error(Net::HTTPServerException) expect(@debug_log).to match(/400/) expect(@debug_log).to match(/HTTP Request Header Data/) expect(@debug_log).to match(/HTTP Status and Header Data/) expect(@debug_log).to match(/HTTP Request Body/) expect(@debug_log).to match(/hithere/) expect(@debug_log).to match(/HTTP Response Body/) expect(@debug_log).to match(/Your request is just terrible./) end end end chef-12.3.0/spec/functional/util/0000755000004100000410000000000012520074675016613 5ustar www-datawww-datachef-12.3.0/spec/functional/util/path_helper_spec.rb0000644000004100000410000000234012520074675022444 0ustar www-datawww-data# # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'tmpdir' require 'chef/util/path_helper' require 'spec_helper' describe Chef::Util::PathHelper, "escape_glob" do PathHelper = Chef::Util::PathHelper it "escapes the glob metacharacters so globbing succeeds" do # make a dir Dir.mktmpdir("\\silly[dir]") do |dir| # add some files files = ["some.rb", "file.txt", "names.csv"] files.each do |file| File.new(File.join(dir, file), 'w').close end pattern = File.join(PathHelper.escape_glob(dir), "*") expect(Dir.glob(pattern).map { |x| File.basename(x) }).to match_array(files) end end end chef-12.3.0/spec/functional/util/powershell/0000755000004100000410000000000012520074675020777 5ustar www-datawww-datachef-12.3.0/spec/functional/util/powershell/cmdlet_spec.rb0000644000004100000410000001075012520074675023611 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/json_compat' require File.expand_path('../../../../spec_helper', __FILE__) describe Chef::Util::Powershell::Cmdlet, :windows_powershell_dsc_only do before(:all) do ohai = Ohai::System.new ohai.load_plugins ohai.run_plugins(true, ['platform', 'kernel']) @node = Chef::Node.new @node.consume_external_attrs(ohai.data, {}) end let(:cmd_output_format) { :text } let(:simple_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, 'get-childitem', cmd_output_format, {:depth => 2}) } let(:invalid_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, 'get-idontexist', cmd_output_format) } let(:cmdlet_get_item_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, 'get-item', cmd_output_format, {:depth => 2}) } let(:cmdlet_alias_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, 'alias', cmd_output_format, {:depth => 2}) } let(:etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" } let(:architecture_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "$env:PROCESSOR_ARCHITECTURE")} it "executes a simple process" do result = simple_cmdlet.run expect(result.succeeded?).to eq(true) end it "#run does not raise a PowershellCmdletException exception if the command cannot be executed" do expect {invalid_cmdlet.run}.not_to raise_error end it "#run! raises a PowershellCmdletException exception if the command cannot be executed" do expect {invalid_cmdlet.run!}.to raise_error(Chef::Exceptions::PowershellCmdletException) end it "executes a 64-bit command on a 64-bit OS, 32-bit otherwise" do os_arch = ENV['PROCESSOR_ARCHITEW6432'] if os_arch.nil? os_arch = ENV['PROCESSOR_ARCHITECTURE'] end result = architecture_cmdlet.run execution_arch = result.return_value execution_arch.strip! expect(execution_arch).to eq(os_arch) end it "passes command line switches to the command" do result = cmdlet_alias_requires_switch_or_argument.run({:name => 'ls'}) expect(result.succeeded?).to eq(true) end it "passes command line arguments to the command" do result = cmdlet_alias_requires_switch_or_argument.run({},{},'ls') expect(result.succeeded?).to eq(true) end it "passes command line arguments and switches to the command" do result = cmdlet_get_item_requires_switch_or_argument.run({:path => etc_directory},{},' | select-object -property fullname | format-table -hidetableheaders') expect(result.succeeded?).to eq(true) returned_directory = result.return_value returned_directory.strip! expect(returned_directory).to eq(etc_directory) end it "passes execution options to the command" do result = cmdlet_get_item_requires_switch_or_argument.run({},{:cwd => etc_directory},'. | select-object -property fullname | format-table -hidetableheaders') expect(result.succeeded?).to eq(true) returned_directory = result.return_value returned_directory.strip! expect(returned_directory).to eq(etc_directory) end context "when returning json" do let(:cmd_output_format) { :json } it "returns json format data" do result = cmdlet_alias_requires_switch_or_argument.run({},{},'ls') expect(result.succeeded?).to eq(true) expect(lambda{Chef::JSONCompat.parse(result.return_value)}).not_to raise_error end end context "when returning Ruby objects" do let(:cmd_output_format) { :object } it "returns object format data" do result = simple_cmdlet.run({},{:cwd => etc_directory}, 'hosts') expect(result.succeeded?).to eq(true) data = result.return_value expect(data['Name']).to eq('hosts') end end context "when constructor is given invalid arguments" do let(:cmd_output_format) { :invalid } it "throws an exception if an invalid format is passed to the constructor" do expect(lambda{simple_cmdlet}).to raise_error end end end chef-12.3.0/spec/functional/shell_spec.rb0000644000004100000410000001056312520074675020311 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'functional/resource/base' require 'chef/version' require 'chef/shell' require 'chef/mixin/command/unix' describe Shell do # chef-shell's unit tests are by necessity very mock-heavy, and frequently do # not catch cases where chef-shell fails to boot because of changes in # chef/client.rb describe "smoke tests", :unix_only => true do include Chef::Mixin::Command::Unix def read_until(io, expected_value) start = Time.new buffer = "" until buffer.include?(expected_value) begin buffer << io.read_nonblock(1) rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EIO, EOFError sleep 0.01 end if Time.new - start > 30 STDERR.puts "did not read expected value `#{expected_value}' within 15s" STDERR.puts "Buffer so far: `#{buffer}'" break end end buffer end def wait_or_die(pid) start = Time.new until exitstatus = Process.waitpid2(pid, Process::WNOHANG) if Time.new - start > 5 STDERR.puts("chef-shell tty did not exit cleanly, killing it") Process.kill(:KILL, pid) end sleep 0.01 end exitstatus[1] end def run_chef_shell_with(options) case ohai[:platform] when "aix" config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA) path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__) output = '' status = popen4("#{path_to_chef_shell} -c #{config} #{options}", :waitlast => true) do |pid, stdin, stdout, stderr| read_until(stdout, "chef >") yield stdout, stdin if block_given? stdin.write("'done'\n") output = read_until(stdout, '=> "done"') stdin.print("exit\n") read_until(stdout, "\n") end [output, status.exitstatus] else # Windows ruby installs don't (always?) have PTY, # so hide the require here begin require 'pty' config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA) path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__) reader, writer, pid = PTY.spawn("#{path_to_chef_shell} -c #{config} #{options}") read_until(reader, "chef >") yield reader, writer if block_given? writer.puts('"done"') output = read_until(reader, '=> "done"') writer.print("exit\n") read_until(reader, "exit") read_until(reader, "\n") read_until(reader, "\n") writer.close exitstatus = wait_or_die(pid) [output, exitstatus] rescue PTY::ChildExited => e [output, e.status] end end end it "boots correctly with -lauto" do output, exitstatus = run_chef_shell_with("-lauto") expect(output).to include("done") expect(exitstatus).to eq(0) end it "sets the log_level from the command line" do output, exitstatus = run_chef_shell_with("-lfatal") do |out, keyboard| show_log_level_code = %q[puts "===#{Chef::Log.level}==="] keyboard.puts(show_log_level_code) read_until(out, show_log_level_code) end expect(output).to include("===fatal===") expect(exitstatus).to eq(0) end it "sets the override_runlist from the command line" do output, exitstatus = run_chef_shell_with("-o 'override::foo,override::bar'") do |out, keyboard| show_recipes_code = %q[puts "#{node.recipes.inspect}"] keyboard.puts(show_recipes_code) read_until(out, show_recipes_code) end expect(output).to include(%q{["override::foo", "override::bar"]}) expect(exitstatus).to eq(0) end end end chef-12.3.0/spec/functional/event_loggers/0000755000004100000410000000000012520074675020501 5ustar www-datawww-datachef-12.3.0/spec/functional/event_loggers/windows_eventlog_spec.rb0000644000004100000410000000775412520074675025452 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' require 'securerandom' require 'chef/event_loggers/windows_eventlog' if Chef::Platform.windows? and not Chef::Platform::windows_server_2003? require 'win32/eventlog' include Win32 end describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_on_win2k3 do let(:run_id) { SecureRandom.uuid } let(:version) { SecureRandom.uuid } let(:elapsed_time) { SecureRandom.random_number(100) } let(:logger) { Chef::EventLoggers::WindowsEventLogger.new } let(:flags) { nil } let(:node) { nil } let(:run_status) { double('Run Status', {run_id: run_id, elapsed_time: elapsed_time }) } let(:event_log) { EventLog.new("Application") } let!(:offset) { event_log.read_last_event.record_number } let(:mock_exception) { double('Exception', {message: SecureRandom.uuid, backtrace:[SecureRandom.uuid, SecureRandom.uuid]})} it 'is available' do expect(Chef::EventLoggers::WindowsEventLogger.available?).to be_truthy end it 'writes run_start event with event_id 10000 and contains version' do logger.run_start(version) expect(event_log.read(flags, offset).any? { |e| e.source == 'Chef' && e.event_id == 10000 && e.string_inserts[0].include?(version)}).to be_truthy end it 'writes run_started event with event_id 10001 and contains the run_id' do logger.run_started(run_status) expect(event_log.read(flags, offset).any? { |e| e.source == 'Chef' && e.event_id == 10001 && e.string_inserts[0].include?(run_id)}).to be_truthy end it 'writes run_completed event with event_id 10002 and contains the run_id and elapsed time' do logger.run_started(run_status) logger.run_completed(node) expect(event_log.read(flags, offset).any? { |e| e.source == 'Chef' && e.event_id == 10002 && e.string_inserts[0].include?(run_id) && e.string_inserts[1].include?(elapsed_time.to_s) }).to be_truthy end it 'writes run_failed event with event_id 10003 and contains the run_id, elapsed time, and exception info' do logger.run_started(run_status) logger.run_failed(mock_exception) expect(event_log.read(flags, offset).any? do |e| e.source == 'Chef' && e.event_id == 10003 && e.string_inserts[0].include?(run_id) && e.string_inserts[1].include?(elapsed_time.to_s) && e.string_inserts[2].include?(mock_exception.class.name) && e.string_inserts[3].include?(mock_exception.message) && e.string_inserts[4].include?(mock_exception.backtrace[0]) && e.string_inserts[4].include?(mock_exception.backtrace[1]) end).to be_truthy end it 'writes run_failed event with event_id 10003 even when run_status is not set' do logger.run_failed(mock_exception) expect(event_log.read(flags, offset).any? do |e| e.source == 'Chef' && e.event_id == 10003 && e.string_inserts[0].include?("UNKNOWN") && e.string_inserts[1].include?("UNKNOWN") && e.string_inserts[2].include?(mock_exception.class.name) && e.string_inserts[3].include?(mock_exception.message) && e.string_inserts[4].include?(mock_exception.backtrace[0]) && e.string_inserts[4].include?(mock_exception.backtrace[1]) end).to be_truthy end end chef-12.3.0/lib/0000755000004100000410000000000012520074675013310 5ustar www-datawww-datachef-12.3.0/lib/chef/0000755000004100000410000000000012520074675014215 5ustar www-datawww-datachef-12.3.0/lib/chef/mixins.rb0000644000004100000410000000070012520074675016046 0ustar www-datawww-datarequire 'chef/mixin/shell_out' require 'chef/mixin/checksum' require 'chef/mixin/command' require 'chef/mixin/convert_to_class_name' require 'chef/mixin/create_path' require 'chef/mixin/deep_merge' require 'chef/mixin/enforce_ownership_and_permissions' require 'chef/mixin/from_file' require 'chef/mixin/params_validate' require 'chef/mixin/path_sanity' require 'chef/mixin/template' require 'chef/mixin/securable' require 'chef/mixin/xml_escape' chef-12.3.0/lib/chef/mixin/0000755000004100000410000000000012520074675015341 5ustar www-datawww-datachef-12.3.0/lib/chef/mixin/checksum.rb0000644000004100000410000000155412520074675017475 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'digest/sha2' require 'chef/digester' class Chef module Mixin module Checksum def checksum(file) Chef::Digester.checksum_for_file(file) end end end end chef-12.3.0/lib/chef/mixin/language_include_attribute.rb0000644000004100000410000000207512520074675023243 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 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/dsl/include_attribute' require 'chef/mixin/deprecation' class Chef module Mixin # DEPRECATED: This is just here for compatibility, use # Chef::DSL::IncludeAttribute instead. deprecate_constant(:LanguageIncludeAttribute, Chef::DSL::IncludeAttribute, <<-EOM) Chef::Mixin::LanguageIncludeAttribute is deprecated. Use Chef::DSL::IncludeAttribute instead. EOM end end chef-12.3.0/lib/chef/mixin/windows_env_helper.rb0000644000004100000410000000353512520074675021575 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 'chef/platform/query_helpers' require 'chef/win32/error' if Chef::Platform.windows? require 'chef/win32/api/system' if Chef::Platform.windows? class Chef module Mixin module WindowsEnvHelper if Chef::Platform.windows? include Chef::ReservedNames::Win32::API::System end #see: http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx HWND_BROADCAST = 0xffff WM_SETTINGCHANGE = 0x001A SMTO_BLOCK = 0x0001 SMTO_ABORTIFHUNG = 0x0002 SMTO_NOTIMEOUTIFNOTHUNG = 0x0008 def broadcast_env_change flags = SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG SendMessageTimeoutA(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string('Environment').address, flags, 5000, nil) end def expand_path(path) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms724265%28v=vs.85%29.aspx # Max size of env block on windows is 32k buf = 0.chr * 32 * 1024 if ExpandEnvironmentStringsA(path, buf, buf.length) == 0 Chef::ReservedNames::Win32::Error.raise! end buf.strip end end end end chef-12.3.0/lib/chef/mixin/deprecation.rb0000644000004100000410000000674212520074675020174 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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. # class Chef module Mixin def self.deprecated_constants @deprecated_constants ||= {} end # Add a deprecated constant to the Chef::Mixin namespace. # === Arguments # * name: the constant name, as a relative symbol. # * replacement: the constant to return instead. # * message: A message telling the user what to do instead. # === Example: # deprecate_constant(:RecipeDefinitionDSLCore, Chef::DSL::Recipe, <<-EOM) # Chef::Mixin::RecipeDefinitionDSLCore is deprecated, use Chef::DSL::Recipe instead. # EOM def self.deprecate_constant(name, replacement, message) deprecated_constants[name] = {:replacement => replacement, :message => message} end # Const missing hook to look up deprecated constants defined with # deprecate_constant. Emits a warning to the logger and returns the # replacement constant. Will call super, most likely causing an exception # for the missing constant, if +name+ is not found in the # deprecated_constants collection. def self.const_missing(name) if new_const = deprecated_constants[name] Chef::Log.warn(new_const[:message]) Chef::Log.warn("Called from: \n#{caller[0...3].map {|l| "\t#{l}"}.join("\n")}") new_const[:replacement] else super end end module Deprecation class DeprecatedObjectProxyBase KEEPERS = %w{__id__ __send__ instance_eval == equal? initialize object_id} instance_methods.each { |method_name| undef_method(method_name) unless KEEPERS.include?(method_name.to_s)} end class DeprecatedInstanceVariable < DeprecatedObjectProxyBase def initialize(target, ivar_name, level=nil) @target, @ivar_name = target, ivar_name @level ||= :warn end def method_missing(method_name, *args, &block) log_deprecation_msg(caller[0..3]) @target.send(method_name, *args, &block) end def inspect @target.inspect end private def log_deprecation_msg(*called_from) called_from = called_from.flatten log("Accessing #{@ivar_name} by the variable @#{@ivar_name} is deprecated. Support will be removed in a future release.") log("Please update your cookbooks to use #{@ivar_name} in place of @#{@ivar_name}. Accessed from:") called_from.each {|l| log(l)} end def log(msg) # WTF: I don't get the log prefix (i.e., "[timestamp] LEVEL:") if I # send to Chef::Log. No one but me should use method_missing, ever. Chef::Log.logger.send(@level, msg) end end def deprecated_ivar(obj, name, level=nil) DeprecatedInstanceVariable.new(obj, name, level) end end end end chef-12.3.0/lib/chef/mixin/xml_escape.rb0000644000004100000410000001151312520074675020007 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2005 Sam Ruby # 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. #-- # Portions of this code are adapted from Sam Ruby's xchar.rb # http://intertwingly.net/stories/2005/09/28/xchar.rb # # Such code appears here under Sam's original MIT license, while portions of # this file are covered by the above Apache License. For a completely MIT # licensed version, please see Sam's original. # # Thanks, Sam! # # Copyright (c) 2005, Sam Ruby # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. require 'chef/log' begin require 'fast_xs' rescue LoadError Chef::Log.info "The fast_xs gem is not installed, slower pure ruby XML escaping will be used." end class Chef module Mixin module XMLEscape module PureRuby extend self CP1252 = { 128 => 8364, # euro sign 130 => 8218, # single low-9 quotation mark 131 => 402, # latin small letter f with hook 132 => 8222, # double low-9 quotation mark 133 => 8230, # horizontal ellipsis 134 => 8224, # dagger 135 => 8225, # double dagger 136 => 710, # modifier letter circumflex accent 137 => 8240, # per mille sign 138 => 352, # latin capital letter s with caron 139 => 8249, # single left-pointing angle quotation mark 140 => 338, # latin capital ligature oe 142 => 381, # latin capital letter z with caron 145 => 8216, # left single quotation mark 146 => 8217, # right single quotation mark 147 => 8220, # left double quotation mark 148 => 8221, # right double quotation mark 149 => 8226, # bullet 150 => 8211, # en dash 151 => 8212, # em dash 152 => 732, # small tilde 153 => 8482, # trade mark sign 154 => 353, # latin small letter s with caron 155 => 8250, # single right-pointing angle quotation mark 156 => 339, # latin small ligature oe 158 => 382, # latin small letter z with caron 159 => 376 # latin capital letter y with diaeresis } # http://www.w3.org/TR/REC-xml/#dt-chardata PREDEFINED = { 38 => '&', # ampersand 60 => '<', # left angle bracket 62 => '>' # right angle bracket } # http://www.w3.org/TR/REC-xml/#charsets VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF), (0xE000..0xFFFD), (0x10000..0x10FFFF)] def xml_escape(unescaped_str) begin unescaped_str.unpack("U*").map {|char| xml_escape_char!(char)}.join rescue unescaped_str.unpack("C*").map {|char| xml_escape_char!(char)}.join end end private def xml_escape_char!(char) char = CP1252[char] || char char = 42 unless VALID.detect {|range| range.include? char} char = PREDEFINED[char] || (char<128 ? char.chr : "&##{char};") end end module FastXS extend self def xml_escape(string) string.fast_xs end end if "strings".respond_to?(:fast_xs) include FastXS extend FastXS else include PureRuby extend PureRuby end end end end chef-12.3.0/lib/chef/mixin/shell_out.rb0000644000004100000410000001031712520074675017666 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'mixlib/shellout' class Chef module Mixin module ShellOut # shell_out! runs a command on the system and will raise an error if the command fails, which is what you want # for debugging, shell_out and shell_out! both will display command output to the tty when the log level is debug # Generally speaking, 'extend Chef::Mixin::ShellOut' in your recipes and include 'Chef::Mixin::ShellOut' in your LWRPs # You can also call Mixlib::Shellout.new directly, but you lose all of the above functionality # we use 'en_US.UTF-8' by default because we parse localized strings in English as an API and # generally must support UTF-8 unicode. def shell_out(*command_args) args = command_args.dup if args.last.is_a?(Hash) options = args.pop.dup env_key = options.has_key?(:env) ? :env : :environment options[env_key] ||= {} options[env_key] = options[env_key].dup options[env_key]['LC_ALL'] ||= Chef::Config[:internal_locale] unless options[env_key].has_key?('LC_ALL') options[env_key]['LANGUAGE'] ||= Chef::Config[:internal_locale] unless options[env_key].has_key?('LANGUAGE') options[env_key]['LANG'] ||= Chef::Config[:internal_locale] unless options[env_key].has_key?('LANG') args << options else args << { :environment => { 'LC_ALL' => Chef::Config[:internal_locale], 'LANGUAGE' => Chef::Config[:internal_locale], 'LANG' => Chef::Config[:internal_locale], } } end shell_out_command(*args) end # call shell_out (using en_US.UTF-8) and raise errors def shell_out!(*command_args) cmd = shell_out(*command_args) cmd.error! cmd end def shell_out_with_systems_locale(*command_args) shell_out_command(*command_args) end def shell_out_with_systems_locale!(*command_args) cmd = shell_out_with_systems_locale(*command_args) cmd.error! cmd end DEPRECATED_OPTIONS = [ [:command_log_level, :log_level], [:command_log_prepend, :log_tag] ] # CHEF-3090: Deprecate command_log_level and command_log_prepend # Patterned after https://github.com/opscode/chef/commit/e1509990b559984b43e428d4d801c394e970f432 def run_command_compatible_options(command_args) return command_args unless command_args.last.is_a?(Hash) my_command_args = command_args.dup my_options = my_command_args.last DEPRECATED_OPTIONS.each do |old_option, new_option| # Edge case: someone specifies :command_log_level and 'command_log_level' in the option hash next unless value = my_options.delete(old_option) || my_options.delete(old_option.to_s) deprecate_option old_option, new_option my_options[new_option] = value end return my_command_args end private def shell_out_command(*command_args) cmd = Mixlib::ShellOut.new(*run_command_compatible_options(command_args)) cmd.live_stream ||= io_for_live_stream cmd.run_command cmd end def deprecate_option(old_option, new_option) Chef::Log.logger.warn "DEPRECATION: Chef::Mixin::ShellOut option :#{old_option} is deprecated. Use :#{new_option}" end def io_for_live_stream if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.debug? STDOUT else nil end end end end end # Break circular dep require 'chef/config' chef-12.3.0/lib/chef/mixin/command.rb0000644000004100000410000001647612520074675017322 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/log' require 'chef/exceptions' require 'tmpdir' require 'fcntl' require 'etc' class Chef module Mixin #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # NOTE: # The popen4 method upon which all the code here is based has a race # condition where it may fail to read all of the data written to stdout and # stderr after the child process exits. The tests for the code here # occasionally fail because of this race condition, so they have been # tagged "volatile". # # This code is considered deprecated, so it should not need to be modified # frequently, if at all. HOWEVER, if you do modify the code here, you must # explicitly enable volatile tests: # # bundle exec rspec spec/unit/mixin/command_spec.rb -t volatile # # In addition, you should make a note that tests need to be run with # volatile tests enabled on any pull request or bug report you submit with # your patch. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! module Command extend self # NOTE: run_command is deprecated in favor of using Chef::Shellout which now comes from the mixlib-shellout gem. NOTE # if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/mixin/command/windows' include ::Chef::Mixin::Command::Windows extend ::Chef::Mixin::Command::Windows else require 'chef/mixin/command/unix' include ::Chef::Mixin::Command::Unix extend ::Chef::Mixin::Command::Unix end # === Parameters # args: A number of required and optional arguments # command, : A complete command with options to execute or a command and options as an Array # creates: The absolute path to a file that prevents the command from running if it exists # cwd: Working directory to execute command in, defaults to Dir.tmpdir # timeout: How many seconds to wait for the command to execute before timing out # returns: The single exit value command is expected to return, otherwise causes an exception # ignore_failure: Whether to raise an exception on failure, or just return the status # output_on_failure: Return output in raised exception regardless of Log.level # # user: The UID or user name of the user to execute the command as # group: The GID or group name of the group to execute the command as # environment: Pairs of environment variable names and their values to set before execution # # === Returns # Returns the exit status of args[:command] def run_command(args={}) status, stdout, stderr = run_command_and_return_stdout_stderr(args) status end # works same as above, except that it returns stdout and stderr # requirement => platforms like solaris 9,10 has weird issues where # even in command failure the exit code is zero, so we need to lookup stderr. def run_command_and_return_stdout_stderr(args={}) command_output = "" args[:ignore_failure] ||= false args[:output_on_failure] ||= false # TODO: This is the wrong place for this responsibility. if args.has_key?(:creates) if File.exists?(args[:creates]) Chef::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.") return false end end status, stdout, stderr = output_of_command(args[:command], args) command_output << "STDOUT: #{stdout}" command_output << "STDERR: #{stderr}" handle_command_failures(status, command_output, args) return status, stdout, stderr end def output_of_command(command, args) Chef::Log.debug("Executing #{command}") stderr_string, stdout_string, status = "", "", nil exec_processing_block = lambda do |pid, stdin, stdout, stderr| stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp end args[:cwd] ||= Dir.tmpdir unless ::File.directory?(args[:cwd]) raise Chef::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory" end Dir.chdir(args[:cwd]) do if args[:timeout] begin Timeout.timeout(args[:timeout]) do status = popen4(command, args, &exec_processing_block) end rescue Timeout::Error => e Chef::Log.error("#{command} exceeded timeout #{args[:timeout]}") raise(e) end else status = popen4(command, args, &exec_processing_block) end Chef::Log.debug("---- Begin output of #{command} ----") Chef::Log.debug("STDOUT: #{stdout_string}") Chef::Log.debug("STDERR: #{stderr_string}") Chef::Log.debug("---- End output of #{command} ----") Chef::Log.debug("Ran #{command} returned #{status.exitstatus}") end return status, stdout_string, stderr_string end def handle_command_failures(status, command_output, opts={}) return if opts[:ignore_failure] opts[:returns] ||= 0 return if Array(opts[:returns]).include?(status.exitstatus) # if the log level is not debug, through output of command when we fail output = "" if Chef::Log.level == :debug || opts[:output_on_failure] output << "\n---- Begin output of #{opts[:command]} ----\n" output << command_output.to_s output << "\n---- End output of #{opts[:command]} ----\n" end raise Chef::Exceptions::Exec, "#{opts[:command]} returned #{status.exitstatus}, expected #{opts[:returns]}#{output}" end # Call #run_command but set LC_ALL to the system's current environment so it doesn't get changed to C. # # === Parameters # args: A number of required and optional arguments that will be handed out to #run_command # # === Returns # Returns the result of #run_command def run_command_with_systems_locale(args={}) args[:environment] ||= {} args[:environment]["LC_ALL"] = ENV["LC_ALL"] run_command args end # def popen4(cmd, args={}, &b) # @@os_handler.popen4(cmd, args, &b) # end # module_function :popen4 def chdir_or_tmpdir(dir, &block) dir ||= Dir.tmpdir unless File.directory?(dir) raise Chef::Exceptions::Exec, "#{dir} does not exist or is not a directory" end Dir.chdir(dir) do block.call end end end end end chef-12.3.0/lib/chef/mixin/deep_merge.rb0000644000004100000410000001166412520074675017772 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Steve Midgley (http://www.misuse.org/science) # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2008 Steve Midgley # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class Chef module Mixin # == Chef::Mixin::DeepMerge # Implements a deep merging algorithm for nested data structures. # ==== Notice: # This code was originally imported from deep_merge by Steve Midgley. # deep_merge is available under the MIT license from # http://trac.misuse.org/science/wiki/DeepMerge module DeepMerge extend self def merge(first, second) first = Mash.new(first) unless first.kind_of?(Mash) second = Mash.new(second) unless second.kind_of?(Mash) DeepMerge.deep_merge(second, first) end class InvalidParameter < StandardError; end # Deep Merge core documentation. # deep_merge! method permits merging of arbitrary child elements. The two top level # elements must be hashes. These hashes can contain unlimited (to stack limit) levels # of child elements. These child elements to not have to be of the same types. # Where child elements are of the same type, deep_merge will attempt to merge them together. # Where child elements are not of the same type, deep_merge will skip or optionally overwrite # the destination element with the contents of the source element at that level. # So if you have two hashes like this: # source = {:x => [1,2,3], :y => 2} # dest = {:x => [4,5,'6'], :y => [7,8,9]} # dest.deep_merge!(source) # Results: {:x => [1,2,3,4,5,'6'], :y => 2} # By default, "deep_merge!" will overwrite any unmergeables and merge everything else. # To avoid this, use "deep_merge" (no bang/exclamation mark) def deep_merge!(source, dest) # if dest doesn't exist, then simply copy source to it if dest.nil? dest = source; return dest end case source when nil dest when Hash if dest.kind_of?(Hash) source.each do |src_key, src_value| if dest[src_key] dest[src_key] = deep_merge!(src_value, dest[src_key]) else # dest[src_key] doesn't exist so we take whatever source has dest[src_key] = src_value end end else # dest isn't a hash, so we overwrite it completely dest = source end when Array if dest.kind_of?(Array) dest = dest | source else dest = source end when String dest = source else # src_hash is not an array or hash, so we'll have to overwrite dest dest = source end dest end # deep_merge! def hash_only_merge(merge_onto, merge_with) hash_only_merge!(safe_dup(merge_onto), safe_dup(merge_with)) end def safe_dup(thing) thing.dup rescue TypeError thing end # Deep merge without Array merge. # `merge_onto` is the object that will "lose" in case of conflict. # `merge_with` is the object whose values will replace `merge_onto`s # values when there is a conflict. def hash_only_merge!(merge_onto, merge_with) # If there are two Hashes, recursively merge. if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash) merge_with.each do |key, merge_with_value| value = if merge_onto.has_key?(key) hash_only_merge(merge_onto[key], merge_with_value) else merge_with_value end if merge_onto.respond_to?(:public_method_that_only_deep_merge_should_use) # we can't call ImmutableMash#[]= because its immutable, but we need to mutate it to build it in-place merge_onto.public_method_that_only_deep_merge_should_use(key, value) else merge_onto[key] = value end end merge_onto # If merge_with is nil, don't replace merge_onto elsif merge_with.nil? merge_onto # In all other cases, replace merge_onto with merge_with else merge_with end end def deep_merge(source, dest) deep_merge!(safe_dup(source), safe_dup(dest)) end end end end chef-12.3.0/lib/chef/mixin/powershell_type_coercions.rb0000644000004100000410000000446712520074675023172 0ustar www-datawww-data# # Author:: Adam Edwards () # Author:: Jay Mundrawala () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef module Mixin module PowershellTypeCoercions def type_coercions @type_coercions ||= { Fixnum => { :type => lambda { |x| x.to_s }}, Float => { :type => lambda { |x| x.to_s }}, FalseClass => { :type => lambda { |x| '$false' }}, TrueClass => { :type => lambda { |x| '$true' }}, Hash => {:type => Proc.new { |x| translate_hash(x)}}, Array => {:type => Proc.new { |x| translate_array(x)}} } end def translate_type(value) translation = type_coercions[value.class] if translation translation[:type].call(value) elsif value.respond_to? :to_psobject "(#{value.to_psobject})" else safe_string(value.to_s) end end private def translate_hash(x) translated = x.inject([]) do |memo, (k,v)| memo << "#{k}=#{translate_type(v)}" end "@{#{translated.join(';')}}" end def translate_array(x) translated = x.map do |v| translate_type(v) end "@(#{translated.join(',')})" end def unsafe?(s) ["'", '#', '`', '"'].any? do |x| s.include? x end end def safe_string(s) # do we need to worry about binary data? if unsafe?(s) encoded_str = Base64.strict_encode64(s.encode("UTF-8")) "([System.Text.Encoding]::UTF8.GetString("\ "[System.Convert]::FromBase64String('#{encoded_str}')"\ "))" else "'#{s}'" end end end end end chef-12.3.0/lib/chef/mixin/command/0000755000004100000410000000000012520074675016757 5ustar www-datawww-datachef-12.3.0/lib/chef/mixin/command/windows.rb0000644000004100000410000000376512520074675021011 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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 'open3' class Chef module Mixin module Command module Windows def popen4(cmd, args={}, &b) # By default, we are waiting before we yield the block. args[:waitlast] ||= false #XXX :user, :group, :environment support? Open3.popen3(cmd) do |stdin,stdout,stderr,cid| if b if args[:waitlast] b[cid, stdin, stdout, stderr] # send EOF so that if the child process is reading from STDIN # it will actually finish up and exit stdin.close_write else o = StringIO.new e = StringIO.new stdin.close stdout.sync = true stderr.sync = true line = stdout.gets(nil) if line o.write(line) end line = stderr.gets(nil) if line e.write(line) end o.rewind e.rewind b[cid, stdin, o, e] end else [cid, stdin, stdout, stderr] end end $? end end end end end chef-12.3.0/lib/chef/mixin/command/unix.rb0000644000004100000410000001606312520074675020275 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef module Mixin module Command module Unix # This is taken directly from Ara T Howard's Open4 library, and then # modified to suit the needs of Chef. Any bugs here are most likely # my own, and not Ara's. # # The original appears in external/open4.rb in its unmodified form. # # Thanks Ara! def popen4(cmd, args={}, &b) # Ruby 1.8 suffers from intermittent segfaults believed to be due to GC while IO.select # See CHEF-2916 / CHEF-1305 GC.disable # Waitlast - this is magic. # # Do we wait for the child process to die before we yield # to the block, or after? That is the magic of waitlast. # # By default, we are waiting before we yield the block. args[:waitlast] ||= false args[:user] ||= nil unless args[:user].kind_of?(Integer) args[:user] = Etc.getpwnam(args[:user]).uid if args[:user] end args[:group] ||= nil unless args[:group].kind_of?(Integer) args[:group] = Etc.getgrnam(args[:group]).gid if args[:group] end args[:environment] ||= {} # Default on C locale so parsing commands output can be done # independently of the node's default locale. # "LC_ALL" could be set to nil, in which case we also must ignore it. unless args[:environment].has_key?("LC_ALL") args[:environment]["LC_ALL"] = "C" end pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe verbose = $VERBOSE begin $VERBOSE = nil ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) cid = fork { pw.last.close STDIN.reopen pw.first pw.first.close pr.first.close STDOUT.reopen pr.last pr.last.close pe.first.close STDERR.reopen pe.last pe.last.close STDOUT.sync = STDERR.sync = true if args[:group] Process.egid = args[:group] Process.gid = args[:group] end if args[:user] Process.euid = args[:user] Process.uid = args[:user] end args[:environment].each do |key,value| ENV[key] = value end if args[:umask] umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777) File.umask(umask) end begin if cmd.kind_of?(Array) Kernel.exec(*cmd) else Kernel.exec(cmd) end raise 'forty-two' rescue Exception => e Marshal.dump(e, ps.last) ps.last.flush end ps.last.close unless (ps.last.closed?) exit! } ensure $VERBOSE = verbose end [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close} begin e = Marshal.load ps.first raise(Exception === e ? e : "unknown failure!") rescue EOFError # If we get an EOF error, then the exec was successful 42 ensure ps.first.close end pw.last.sync = true pi = [pw.last, pr.first, pe.first] if b begin if args[:waitlast] b[cid, *pi] # send EOF so that if the child process is reading from STDIN # it will actually finish up and exit pi[0].close_write Process.waitpid2(cid).last else # This took some doing. # The trick here is to close STDIN # Then set our end of the childs pipes to be O_NONBLOCK # Then wait for the child to die, which means any IO it # wants to do must be done - it's dead. If it isn't, # it's because something totally skanky is happening, # and we don't care. o = StringIO.new e = StringIO.new pi[0].close stdout = pi[1] stderr = pi[2] stdout.sync = true stderr.sync = true stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK) stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK) stdout_finished = false stderr_finished = false results = nil while !stdout_finished || !stderr_finished begin channels_to_watch = [] channels_to_watch << stdout if !stdout_finished channels_to_watch << stderr if !stderr_finished ready = IO.select(channels_to_watch, nil, nil, 1.0) rescue Errno::EAGAIN ensure results = Process.waitpid2(cid, Process::WNOHANG) if results stdout_finished = true stderr_finished = true end end if ready && ready.first.include?(stdout) line = results ? stdout.gets(nil) : stdout.gets if line o.write(line) else stdout_finished = true end end if ready && ready.first.include?(stderr) line = results ? stderr.gets(nil) : stderr.gets if line e.write(line) else stderr_finished = true end end end results = Process.waitpid2(cid) unless results o.rewind e.rewind b[cid, pi[0], o, e] results.last end ensure pi.each{|fd| fd.close unless fd.closed?} end else [cid, pw.last, pr.first, pe.first] end ensure GC.enable end end end end end chef-12.3.0/lib/chef/mixin/get_source_from_package.rb0000644000004100000410000000315212520074675022524 0ustar www-datawww-data# Author:: Lamont Granquist () # 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. # # # mixin to make this syntax work without specifying a source: # # gem_pacakge "/tmp/foo-x.y.z.gem" # rpm_package "/tmp/foo-x.y-z.rpm" # dpkg_package "/tmp/foo-x.y.z.deb" # class Chef module Mixin module GetSourceFromPackage def initialize(new_resource, run_context) super return if new_resource.package_name.is_a?(Array) # if we're passed something that looks like a filesystem path, with no source, use it # - require at least one '/' in the path to avoid gem_package "foo" breaking if a file named 'foo' exists in the cwd if new_resource.source.nil? && new_resource.package_name.match(/#{::File::SEPARATOR}/) && ::File.exists?(new_resource.package_name) Chef::Log.debug("No package source specified, but #{new_resource.package_name} exists on the filesystem, copying to package source") new_resource.source(@new_resource.package_name) end end end end end chef-12.3.0/lib/chef/mixin/recipe_definition_dsl_core.rb0000644000004100000410000000224012520074675023215 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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. # ### # NOTE: This file and constant are here only for backwards compatibility. # New code should use Chef::DSL::Recipe instead. # # This constant (module name) will eventually be deprecated and then removed. ### require 'chef/mixin/deprecation' class Chef module Mixin deprecate_constant(:RecipeDefinitionDSLCore, Chef::DSL::Recipe, <<-EOM) Chef::Mixin::RecipeDefinitionDSLCore is deprecated. Use Chef::DSL::Recipe instead. EOM end end chef-12.3.0/lib/chef/mixin/provides.rb0000644000004100000410000000171612520074675017526 0ustar www-datawww-data require 'chef/mixin/descendants_tracker' class Chef module Mixin module Provides include Chef::Mixin::DescendantsTracker def node_map @node_map ||= Chef::NodeMap.new end def provides(short_name, opts={}, &block) if !short_name.kind_of?(Symbol) # YAGNI: this is probably completely unnecessary and can be removed? Chef::Log.deprecation "Passing a non-Symbol to Chef::Resource#provides will be removed" if short_name.kind_of?(String) short_name.downcase! short_name.gsub!(/\s/, "_") end short_name = short_name.to_sym end node_map.set(short_name, true, opts, &block) end # provides a node on the resource (early binding) def provides?(node, resource_name) resource_name = resource_name.resource_name if resource_name.is_a?(Chef::Resource) node_map.get(node, resource_name) end end end end chef-12.3.0/lib/chef/mixin/securable.rb0000644000004100000410000001625512520074675017644 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # class Chef module Mixin module Securable def owner(arg=nil) set_or_return( :owner, arg, :regex => Chef::Config[:user_valid_regex] ) end alias :user :owner def group(arg=nil) set_or_return( :group, arg, :regex => Chef::Config[:group_valid_regex] ) end def mode(arg=nil) set_or_return( :mode, arg, :callbacks => { "not in valid numeric range" => lambda { |m| if m.kind_of?(String) m =~ /^0/ || m="0#{m}" end # Windows does not support the sticky or setuid bits if Chef::Platform.windows? Integer(m)<=0777 && Integer(m)>=0 else Integer(m)<=07777 && Integer(m)>=0 end }, } ) end #==WindowsMacros # Defines methods for adding attributes to a chef resource to describe # Windows file security metadata. # # This module is meant to be used to extend a class (instead of # `include`-ing). A class is automatically extended with this module when # it includes WindowsSecurableAttributes. # -- # TODO should this be separated into different files? module WindowsMacros # === rights_attribute # "meta-method" for dynamically creating rights attributes on resources. # # Multiple rights attributes can be declared. This enables resources to # have multiple rights attributes with separate runtime states. # # For example, +Chef::Resource::RemoteDirectory+ supports different # rights on the directories and files by declaring separate rights # attributes for each (rights and files_rights). # # ==== User Level API # Given a resource that calls # # rights_attribute(:rights) # # Then the resource DSL could be used like this: # # rights :read, ["Administrators","Everyone"] # rights :deny, "Pinky" # rights :full_control, "Users", :applies_to_children => true # rights :write, "John Keiser", :applies_to_children => :containers_only, :applies_to_self => false, :one_level_deep => true # # ==== Internal Data Structure # rights attributes support multiple right declarations # in a single resource block--the data will be merged # into a single internal hash. # # The internal representation is a hash with the following keys: # # * `:permissions`: Integer of Windows permissions flags, 1..2^32 # or one of `[:full_control, :modify, :read_execute, :read, :write]` # * `:principals`: String or Array of Strings represnting usernames on # the system. # * `:applies_to_children` (optional): Boolean # * `:applies_to_self` (optional): Boolean # * `:one_level_deep` (optional): Boolean # def rights_attribute(name) # equivalent to something like: # def rights(permissions=nil, principals=nil, args_hash=nil) define_method(name) do |permissions=nil, principals=nil, args_hash=nil| rights = self.instance_variable_get("@#{name.to_s}".to_sym) unless permissions.nil? input = { :permissions => permissions, :principals => principals } input.merge!(args_hash) unless args_hash.nil? validations = {:permissions => { :required => true }, :principals => { :required => true, :kind_of => [String, Array] }, :applies_to_children => { :equal_to => [ true, false, :containers_only, :objects_only ]}, :applies_to_self => { :kind_of => [ TrueClass, FalseClass ] }, :one_level_deep => { :kind_of => [ TrueClass, FalseClass ] } } validate(input, validations) [ permissions ].flatten.each do |permission| if permission.is_a?(Integer) if permission < 0 || permission > 1<<32 raise ArgumentError, "permissions flags must be positive and <= 32 bits (#{permission})" end elsif !([:full_control, :modify, :read_execute, :read, :write].include?(permission.to_sym)) raise ArgumentError, "permissions parameter must be :full_control, :modify, :read_execute, :read, :write or an integer representing Windows permission flags" end end [ principals ].flatten.each do |principal| if !principal.is_a?(String) raise ArgumentError, "principals parameter must be a string or array of strings representing usernames" end end if input[:applies_to_children] == false if input[:applies_to_self] == false raise ArgumentError, "'rights' attribute must specify either :applies_to_children or :applies_to_self." end if input[:one_level_deep] == true raise ArgumentError, "'rights' attribute specified :one_level_deep without specifying :applies_to_children." end end rights ||= [] rights << input end set_or_return( name, rights, {} ) end end end #==WindowsSecurableAttributes # Defines #inherits to describe Windows file security ACLs on the # including class module WindowsSecurableAttributes def inherits(arg=nil) set_or_return( :inherits, arg, :kind_of => [ TrueClass, FalseClass ] ) end end if RUBY_PLATFORM =~ /mswin|mingw|windows/ include WindowsSecurableAttributes end # Callback that fires when included; will extend the including class # with WindowsMacros and define #rights and #deny_rights on it. def self.included(including_class) if RUBY_PLATFORM =~ /mswin|mingw|windows/ including_class.extend(WindowsMacros) # create a default 'rights' attribute including_class.rights_attribute(:rights) including_class.rights_attribute(:deny_rights) end end end end end chef-12.3.0/lib/chef/mixin/convert_to_class_name.rb0000644000004100000410000000754212520074675022245 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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. # class Chef module Mixin module ConvertToClassName extend self def convert_to_class_name(str) str = str.dup str.gsub!(/[^A-Za-z0-9_]/,'_') str.gsub!(/^(_+)?/,'') rname = nil regexp = %r{^(.+?)(_(.+))?$} mn = str.match(regexp) if mn rname = mn[1].capitalize while mn && mn[3] mn = mn[3].match(regexp) rname << mn[1].capitalize if mn end end rname end def convert_to_snake_case(str, namespace=nil) str = str.dup str.sub!(/^#{namespace}(\:\:)?/, '') if namespace str.gsub!(/[A-Z]/) {|s| "_" + s} str.downcase! str.sub!(/^\_/, "") str end def snake_case_basename(str) with_namespace = convert_to_snake_case(str) with_namespace.split("::").last.sub(/^_/, '') end def filename_to_qualified_string(base, filename) file_base = File.basename(filename, ".rb") base.to_s + (file_base == 'default' ? '' : "_#{file_base}") end # Copied from rails activesupport. In ruby >= 2.0 const_get will just do this, so this can # be deprecated and removed. # # MIT LICENSE is here: https://github.com/rails/rails/blob/master/activesupport/MIT-LICENSE # Tries to find a constant with the name specified in the argument string. # # 'Module'.constantize # => Module # 'Test::Unit'.constantize # => Test::Unit # # The name is assumed to be the one of a top-level constant, no matter # whether it starts with "::" or not. No lexical context is taken into # account: # # C = 'outside' # module M # C = 'inside' # C # => 'inside' # 'C'.constantize # => 'outside', same as ::C # end # # NameError is raised when the name is not in CamelCase or the constant is # unknown. def constantize(camel_cased_word) names = camel_cased_word.split('::') # Trigger a built-in NameError exception including the ill-formed constant in the message. Object.const_get(camel_cased_word) if names.empty? # Remove the first blank element in case of '::ClassName' notation. names.shift if names.size > 1 && names.first.empty? names.inject(Object) do |constant, name| if constant == Object constant.const_get(name) else candidate = constant.const_get(name) next candidate if constant.const_defined?(name, false) next candidate unless Object.const_defined?(name) # Go down the ancestors to check if it is owned directly. The check # stops when we reach Object or the end of ancestors tree. constant = constant.ancestors.inject do |const, ancestor| break const if ancestor == Object break ancestor if ancestor.const_defined?(name, false) const end # owner is in Object, so raise constant.const_get(name, false) end end end end end end chef-12.3.0/lib/chef/mixin/path_sanity.rb0000644000004100000410000000405412520074675020214 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # class Chef module Mixin module PathSanity def enforce_path_sanity(env=ENV) if Chef::Config[:enforce_path_sanity] env["PATH"] = "" if env["PATH"].nil? path_separator = Chef::Platform.windows? ? ';' : ':' existing_paths = env["PATH"].split(path_separator) # ensure the Ruby and Gem bindirs are included # mainly for 'full-stack' Chef installs paths_to_add = [] paths_to_add << ruby_bindir unless sane_paths.include?(ruby_bindir) paths_to_add << gem_bindir unless sane_paths.include?(gem_bindir) paths_to_add << sane_paths if sane_paths paths_to_add.flatten!.compact! paths_to_add.each do |sane_path| unless existing_paths.include?(sane_path) env_path = env["PATH"].dup env_path << path_separator unless env["PATH"].empty? env_path << sane_path env["PATH"] = env_path end end end end private def sane_paths @sane_paths ||= begin if Chef::Platform.windows? %w[] else %w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin] end end end def ruby_bindir RbConfig::CONFIG['bindir'] end def gem_bindir Gem.bindir end end end end chef-12.3.0/lib/chef/mixin/homebrew_user.rb0000644000004100000410000000454512520074675020544 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Graeme Mathieson () # # Copyright 2011-2013, Opscode, Inc. # Copyright 2014, Chef Software, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Ported from the homebrew cookbook's Homebrew::Mixin owner helpers # # This lives here in Chef::Mixin because Chef's namespacing makes it # awkward to use modules elsewhere (e.g., chef/provider/package/homebrew/owner) require 'chef/mixin/shell_out' require 'etc' class Chef module Mixin module HomebrewUser include Chef::Mixin::ShellOut ## # This tries to find the user to execute brew as. If a user is provided, that overrides the brew # executable user. It is an error condition if the brew executable owner is root or we cannot find # the brew executable. def find_homebrew_uid(provided_user = nil) # They could provide us a user name or a UID if provided_user return provided_user if provided_user.is_a? Integer return Etc.getpwnam(provided_user).uid end @homebrew_owner ||= calculate_owner @homebrew_owner end private def calculate_owner default_brew_path = '/usr/local/bin/brew' if ::File.exist?(default_brew_path) # By default, this follows symlinks which is what we want owner = ::File.stat(default_brew_path).uid elsif (brew_path = shell_out("which brew").stdout.strip) && !brew_path.empty? owner = ::File.stat(brew_path).uid else raise Chef::Exceptions::CannotDetermineHomebrewOwner, 'Could not find the "brew" executable in /usr/local/bin or anywhere on the path.' end Chef::Log.debug "Found Homebrew owner #{Etc.getpwuid(owner).name}; executing `brew` commands as them" owner end end end end chef-12.3.0/lib/chef/mixin/which.rb0000644000004100000410000000225412520074675016773 0ustar www-datawww-data#-- # Author:: Lamont Granquist # Copyright:: Copyright (c) 2010 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. class Chef module Mixin module Which def which(cmd, opts = {}) extra_path = if opts[:extra_path].nil? [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ] else [ opts[:extra_path] ].flatten end paths = ENV['PATH'].split(File::PATH_SEPARATOR) + extra_path paths.each do |path| filename = File.join(path, cmd) return filename if File.executable?(filename) end false end end end end chef-12.3.0/lib/chef/mixin/params_validate.rb0000644000004100000410000002176412520074675021034 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. class Chef class DelayedEvaluator < Proc end module Mixin module ParamsValidate # Takes a hash of options, along with a map to validate them. Returns the original # options hash, plus any changes that might have been made (through things like setting # default values in the validation map) # # For example: # # validate({ :one => "neat" }, { :one => { :kind_of => String }}) # # Would raise an exception if the value of :one above is not a kind_of? string. Valid # map options are: # # :default:: Sets the default value for this parameter. # :callbacks:: Takes a hash of Procs, which should return true if the argument is valid. # The key will be inserted into the error message if the Proc does not return true: # "Option #{key}'s value #{value} #{message}!" # :kind_of:: Ensure that the value is a kind_of?(Whatever). If passed an array, it will ensure # that the value is one of those types. # :respond_to:: Ensure that the value has a given method. Takes one method name or an array of # method names. # :required:: Raise an exception if this parameter is missing. Valid values are true or false, # by default, options are not required. # :regex:: Match the value of the parameter against a regular expression. # :equal_to:: Match the value of the parameter with ==. An array means it can be equal to any # of the values. def validate(opts, map) #-- # validate works by taking the keys in the validation map, assuming it's a hash, and # looking for _pv_:symbol as methods. Assuming it find them, it calls the right # one. #++ raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash) raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash) map.each do |key, validation| unless key.kind_of?(Symbol) || key.kind_of?(String) raise ArgumentError, "Validation map keys must be symbols or strings!" end case validation when true _pv_required(opts, key) when false true when Hash validation.each do |check, carg| check_method = "_pv_#{check.to_s}" if self.respond_to?(check_method, true) self.send(check_method, opts, key, carg) else raise ArgumentError, "Validation map has unknown check: #{check}" end end end end opts end def lazy(&block) DelayedEvaluator.new(&block) end def set_or_return(symbol, arg, validation) iv_symbol = "@#{symbol.to_s}".to_sym if arg == nil && self.instance_variable_defined?(iv_symbol) == true ivar = self.instance_variable_get(iv_symbol) if(ivar.is_a?(DelayedEvaluator)) validate({ symbol => ivar.call }, { symbol => validation })[symbol] else ivar end else if(arg.is_a?(DelayedEvaluator)) val = arg else val = validate({ symbol => arg }, { symbol => validation })[symbol] # Handle the case where the "default" was a DelayedEvaluator. In # this case, the block yields an optional parameter of +self+, # which is the equivalent of "new_resource" if val.is_a?(DelayedEvaluator) val = val.call(self) end end self.instance_variable_set(iv_symbol, val) end end private # Return the value of a parameter, or nil if it doesn't exist. def _pv_opts_lookup(opts, key) if opts.has_key?(key.to_s) opts[key.to_s] elsif opts.has_key?(key.to_sym) opts[key.to_sym] else nil end end # Raise an exception if the parameter is not found. def _pv_required(opts, key, is_required=true) if is_required if (opts.has_key?(key.to_s) && !opts[key.to_s].nil?) || (opts.has_key?(key.to_sym) && !opts[key.to_sym].nil?) true else raise Exceptions::ValidationFailed, "Required argument #{key} is missing!" end end end def _pv_equal_to(opts, key, to_be) value = _pv_opts_lookup(opts, key) unless value.nil? passes = false Array(to_be).each do |tb| passes = true if value == tb end unless passes raise Exceptions::ValidationFailed, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}." end end end # Raise an exception if the parameter is not a kind_of?(to_be) def _pv_kind_of(opts, key, to_be) value = _pv_opts_lookup(opts, key) unless value.nil? passes = false Array(to_be).each do |tb| passes = true if value.kind_of?(tb) end unless passes raise Exceptions::ValidationFailed, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}." end end end # Raise an exception if the parameter does not respond to a given set of methods. def _pv_respond_to(opts, key, method_name_list) value = _pv_opts_lookup(opts, key) unless value.nil? Array(method_name_list).each do |method_name| unless value.respond_to?(method_name) raise Exceptions::ValidationFailed, "Option #{key} must have a #{method_name} method!" end end end end # Assert that parameter returns false when passed a predicate method. # For example, :cannot_be => :blank will raise a Exceptions::ValidationFailed # error value.blank? returns a 'truthy' (not nil or false) value. # # Note, this will *PASS* if the object doesn't respond to the method. # So, to make sure a value is not nil and not blank, you need to do # both :cannot_be => :blank *and* :cannot_be => :nil (or :required => true) def _pv_cannot_be(opts, key, predicate_method_base_name) value = _pv_opts_lookup(opts, key) predicate_method = (predicate_method_base_name.to_s + "?").to_sym if value.respond_to?(predicate_method) if value.send(predicate_method) raise Exceptions::ValidationFailed, "Option #{key} cannot be #{predicate_method_base_name}" end end end # Assign a default value to a parameter. def _pv_default(opts, key, default_value) value = _pv_opts_lookup(opts, key) if value == nil opts[key] = default_value end end # Check a parameter against a regular expression. def _pv_regex(opts, key, regex) value = _pv_opts_lookup(opts, key) if value != nil passes = false [ regex ].flatten.each do |r| if value != nil if r.match(value.to_s) passes = true end end end unless passes raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}" end end end # Check a parameter against a hash of proc's. def _pv_callbacks(opts, key, callbacks) raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash) value = _pv_opts_lookup(opts, key) if value != nil callbacks.each do |message, zeproc| if zeproc.call(value) != true raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} #{message}!" end end end end # Allow a parameter to default to @name def _pv_name_attribute(opts, key, is_name_attribute=true) if is_name_attribute if opts[key] == nil opts[key] = self.instance_variable_get("@name") end end end end end end chef-12.3.0/lib/chef/mixin/from_file.rb0000644000004100000410000000322612520074675017633 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # 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. # class Chef module Mixin module FromFile # Loads a given ruby file, and runs instance_eval against it in the context of the current # object. # # Raises an IOError if the file cannot be found, or is not readable. def from_file(filename) if File.exists?(filename) && File.readable?(filename) self.instance_eval(IO.read(filename), filename, 1) else raise IOError, "Cannot open or read #{filename}!" end end # Loads a given ruby file, and runs class_eval against it in the context of the current # object. # # Raises an IOError if the file cannot be found, or is not readable. def class_from_file(filename) if File.exists?(filename) && File.readable?(filename) self.class_eval(IO.read(filename), filename, 1) else raise IOError, "Cannot open or read #{filename}!" end end end end end chef-12.3.0/lib/chef/mixin/create_path.rb0000644000004100000410000000502012520074675020142 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. class Chef module Mixin module CreatePath # Creates a given path, including all directories that lead up to it. # Like mkdir_p, but without the leaking. # # === Parameters # file_path:: A string that represents the path to create, # or an Array with the path-parts. # # === Returns # The created file_path. def create_path(file_path) unless file_path.kind_of?(String) || file_path.kind_of?(Array) raise ArgumentError, "file_path must be a string or an array!" end if file_path.kind_of?(String) file_path = File.expand_path(file_path).split(File::SEPARATOR) file_path.shift if file_path[0] == '' # Check if path starts with a separator or drive letter (Windows) unless file_path[0].match("^#{File::SEPARATOR}|^[a-zA-Z]:") file_path[0] = "#{File::SEPARATOR}#{file_path[0]}" end end file_path.each_index do |i| create_path = File.join(file_path[0, i + 1]) create_dir(create_path) unless File.directory?(create_path) end File.expand_path(File.join(file_path)) end private def create_dir(path) begin # When doing multithreaded downloads into the file cache, the following # interleaving raises an error here: # # thread1 thread2 # File.directory?(create_path) <- false # File.directory?(create_path) <- false # Dir.mkdir(create_path) # Dir.mkdir(create_path) <- raises Errno::EEXIST Chef::Log.debug("Creating directory #{path}") Dir.mkdir(path) rescue Errno::EEXIST end end end end end chef-12.3.0/lib/chef/mixin/windows_architecture_helper.rb0000644000004100000410000001132312520074675023461 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 'chef/platform/query_helpers' require 'win32/api' if Chef::Platform.windows? require 'chef/win32/api/process' if Chef::Platform.windows? require 'chef/win32/api/error' if Chef::Platform.windows? class Chef module Mixin module WindowsArchitectureHelper if Chef::Platform.windows? include Chef::ReservedNames::Win32::API::Process include Chef::ReservedNames::Win32::API::Error end def node_windows_architecture(node) node[:kernel][:machine].to_sym end def wow64_architecture_override_required?(node, desired_architecture) desired_architecture == :x86_64 && node_windows_architecture(node) == :x86_64 && is_i386_process_on_x86_64_windows? end def with_os_architecture(node) node ||= begin os_arch = ENV['PROCESSOR_ARCHITEW6432'] || ENV['PROCESSOR_ARCHITECTURE'] Hash.new.tap do |n| n[:kernel] = Hash.new n[:kernel][:machine] = os_arch == 'AMD64' ? :x86_64 : :i386 end end wow64_redirection_state = nil if wow64_architecture_override_required?(node, node_windows_architecture(node)) wow64_redirection_state = disable_wow64_file_redirection(node) end begin yield ensure if wow64_redirection_state restore_wow64_file_redirection(node, wow64_redirection_state) end end 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_process_on_x86_64_windows? if Chef::Platform.windows? is_64_bit_process_result = FFI::MemoryPointer.new(:int) # The return value of IsWow64Process is nonzero value if the API call succeeds. # The result data are returned in the last parameter, not the return value. call_succeeded = IsWow64Process(GetCurrentProcess(), is_64_bit_process_result) # The result is nonzero if IsWow64Process's calling process, in the case here # this process, is running under WOW64, i.e. the result is nonzero if this # process is 32-bit (aka :i386). result = (call_succeeded != 0) && (is_64_bit_process_result.get_int(0) != 0) else false end 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 chef-12.3.0/lib/chef/mixin/language_include_recipe.rb0000644000004100000410000000170712520074675022510 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 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/dsl/include_recipe' require 'chef/mixin/deprecation' class Chef module Mixin deprecate_constant(:LanguageIncludeRecipe, Chef::DSL::IncludeRecipe, <<-EOM) Chef::Mixin::LanguageIncludeRecipe is deprecated, use Chef::DSL::IncludeRecipe instead. EOM end end chef-12.3.0/lib/chef/mixin/file_class.rb0000644000004100000410000000203212520074675017767 0ustar www-datawww-data# # Author:: Mark Mzyk # Author:: Seth Chisamore # Author:: Bryan McLellan # Copyright:: Copyright (c) 2011-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. # class Chef module Mixin module FileClass def file_class @host_os_file ||= if Chef::Platform.windows? require 'chef/win32/file' Chef::ReservedNames::Win32::File else ::File end end end end end chef-12.3.0/lib/chef/mixin/language.rb0000644000004100000410000000326712520074675017461 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/dsl/platform_introspection' require 'chef/dsl/data_query' require 'chef/mixin/deprecation' class Chef module Mixin # == [DEPRECATED] Chef::Mixin::DeprecatedLanguageModule # This module is a temporary replacement for the previous # Chef::Mixin::Language. That module's functionality was split into two # modules, Chef::DSL::PlatformIntrospection, and Chef::DSL::DataQuery. # # This module includes both PlatformIntrospection and DataQuery to provide # the same interfaces and behavior as the prior Mixin::Language. # # This module is loaded via const_missing hook when Chef::Mixin::Language # is accessed. See chef/mixin/deprecation for details. module DeprecatedLanguageModule include Chef::DSL::PlatformIntrospection include Chef::DSL::DataQuery end deprecate_constant(:Language, DeprecatedLanguageModule, <<-EOM) Chef::Mixin::Language is deprecated. Use either (or both) Chef::DSL::PlatformIntrospection or Chef::DSL::DataQuery instead. EOM end end chef-12.3.0/lib/chef/mixin/descendants_tracker.rb0000644000004100000410000000526012520074675021677 0ustar www-datawww-data# # Copyright (c) 2005-2012 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # This is lifted from rails activesupport (note the copyright above): # https://github.com/rails/rails/blob/9f84e60ac9d7bf07d6ae1bc94f3941f5b8f1a228/activesupport/lib/active_support/descendants_tracker.rb class Chef module Mixin module DescendantsTracker @@direct_descendants = {} class << self def direct_descendants(klass) @@direct_descendants[klass] || [] end def descendants(klass) arr = [] accumulate_descendants(klass, arr) arr end def find_descendants_by_name(klass, name) descendants(klass).first {|c| c.name == name } end # This is the only method that is not thread safe, but is only ever called # during the eager loading phase. def store_inherited(klass, descendant) (@@direct_descendants[klass] ||= []) << descendant end private def accumulate_descendants(klass, acc) if direct_descendants = @@direct_descendants[klass] acc.concat(direct_descendants) direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) } end end end def inherited(base) DescendantsTracker.store_inherited(self, base) super end def direct_descendants DescendantsTracker.direct_descendants(self) end def find_descendants_by_name(name) DescendantsTracker.find_descendants_by_name(self, name) end def descendants DescendantsTracker.descendants(self) end end end end chef-12.3.0/lib/chef/mixin/template.rb0000644000004100000410000001765112520074675017513 0ustar www-datawww-data#-- # Author:: Adam Jacob () # 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 'tempfile' require 'erubis' class Chef module Mixin module Template # == ChefContext # ChefContext was previously used to mix behavior into Erubis::Context so # that it would be available to templates. This behavior has now moved to # TemplateContext, but this module is still mixed in to the # TemplateContext class so that any user code that modified ChefContext # will continue to work correctly. module ChefContext end # == TemplateContext # TemplateContext is the base context class for all templates in Chef. It # defines user-facing extensions to the base Erubis::Context to provide # enhanced features. Individual instances of TemplateContext can be # extended to add logic to a specific template. # class TemplateContext < Erubis::Context include ChefContext attr_reader :_extension_modules def initialize(variables) super @_extension_modules = [] end ### # USER FACING API ### # Returns the current node object, or raises an error if it's not set. # Provides API consistency, allowing users to reference the node object # by the bare `node` everywhere. def node return @node if @node raise "Could not find a value for node. If you are explicitly setting variables in a template, " + "include a node variable if you plan to use it." end # # Takes the name of the partial, plus a hash of options. Returns a # string that contains the result of the evaluation of the partial. # # All variables from the parent template will be propagated down to # the partial, unless you pass the +variables+ option (see below). # # Valid options are: # # :local:: If true then the partial name will be interpreted as the # path to a file on the local filesystem; if false (the # default) it will be looked up in the cookbook according to # the normal rules for templates. # :source:: If specified then the partial will be looked up with this # name or path (according to the +local+ option) instead of # +partial_name+. # :cookbook:: Search for the partial in the provided cookbook instead # of the cookbook that contains the top-level template. # :variables:: A Hash of variable_name => value that will be made # available to the partial. If specified, none of the # variables from the master template will be, so if you # need them you will need to propagate them explicitly. # def render(partial_name, options = {}) raise "You cannot render partials in this context" unless @template_finder partial_variables = options.delete(:variables) || _public_instance_variables partial_context = self.class.new(partial_variables) partial_context._extend_modules(@_extension_modules) template_location = @template_finder.find(partial_name, options) _render_template(IO.binread(template_location), partial_context) end def render_template(template_location) _render_template(IO.binread(template_location), self) end def render_template_from_string(template) _render_template(template, self) end ### # INTERNAL PUBLIC API ### def _render_template(template, context) begin eruby = Erubis::Eruby.new(template) output = eruby.evaluate(context) rescue Object => e raise TemplateError.new(e, template, context) end # CHEF-4399 # Erubis always emits unix line endings during template # rendering. Chef used to convert line endings to the # original line endings in the template. However this # created problems in cases when cookbook developer is # coding the cookbook on windows but using it on non-windows # platforms. # The safest solution is to make sure that native to the # platform we are running on is used in order to minimize # potential issues for the applications that will consume # this template. if Chef::Platform.windows? output = output.gsub(/\r?\n/,"\r\n") end output end def _extend_modules(module_names) module_names.each do |mod| context_methods = [:node, :render, :render_template, :render_template_from_string] context_methods.each do |core_method| if mod.method_defined?(core_method) or mod.private_method_defined?(core_method) Chef::Log.warn("Core template method `#{core_method}' overridden by extension module #{mod}") end end extend(mod) @_extension_modules << mod end end # Collects instance variables set on the current object as a Hash # suitable for creating a new TemplateContext. Instance variables that # are only valid for this specific instance are omitted from the # collection. def _public_instance_variables all_ivars = instance_variables all_ivars.delete(:@_extension_modules) all_ivars.inject({}) do |ivar_map, ivar_symbol_name| value = instance_variable_get(ivar_symbol_name) name_without_at = ivar_symbol_name.to_s[1..-1].to_sym ivar_map[name_without_at] = value ivar_map end end end class TemplateError < RuntimeError attr_reader :original_exception, :context SOURCE_CONTEXT_WINDOW = 2 def initialize(original_exception, template, context) @original_exception, @template, @context = original_exception, template, context end def message @original_exception.message end def line_number @line_number ||= $1.to_i if original_exception.backtrace.find {|line| line =~ /\(erubis\):(\d+)/ } end def source_location "on line ##{line_number}" end def source_listing @source_listing ||= begin lines = @template.split(/\n/) if line_number line_index = line_number - 1 beginning_line = line_index <= SOURCE_CONTEXT_WINDOW ? 0 : line_index - SOURCE_CONTEXT_WINDOW source_size = SOURCE_CONTEXT_WINDOW * 2 + 1 else beginning_line = 0 source_size = lines.length end contextual_lines = lines[beginning_line, source_size] output = [] contextual_lines.each_with_index do |line, index| line_number = (index+beginning_line+1).to_s.rjust(3) output << "#{line_number}: #{line}" end output.join("\n") end end def to_s "\n\n#{self.class} (#{message}) #{source_location}:\n\n" + "#{source_listing}\n\n #{original_exception.backtrace.join("\n ")}\n\n" end end end end end chef-12.3.0/lib/chef/mixin/why_run.rb0000644000004100000410000003426612520074675017374 0ustar www-datawww-data# # Author:: Dan DeLeo ( ) # Author:: Marc Paradise ( ) # 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. # class Chef module Mixin module WhyRun # ==ConvergeActions # ConvergeActions implements the logic for why run. A ConvergeActions # object wraps a collection of actions, which consist of a descriptive # string and a block/Proc. Actions are executed by calling #converge! # When why_run mode is enabled, each action's description will be # printed, but the block will not be called. Conversely, in normal mode, # the block is called, but the message is not printed. # # In general, this class should be accessed through the API provided by # Chef::Provider. class ConvergeActions attr_reader :actions def initialize(resource, run_context, action) @resource, @run_context = resource, run_context @actions = [] end def events @run_context.events end # Adds an action to the list. +descriptions+ can either be an Array of # Strings, or a single String describing the action; +block+ is a # block/proc that implements the action. def add_action(descriptions, &block) @actions << [descriptions, block] if (@resource.respond_to?(:is_guard_interpreter) && @resource.is_guard_interpreter) || !Chef::Config[:why_run] block.call end events.resource_update_applied(@resource, @action, descriptions) end # True if there are no actions to execute. def empty? @actions.empty? end end # == ResourceRequirements # ResourceRequirements provides a framework for making assertions about # the host system's state. It also provides a mechanism for making # assumptions about what the system's state might have been when running # in why run mode. # # For example, consider a recipe that consists of a package resource and # a service resource. If the service's init script is installed by the # package, and Chef is running in why run mode, then the service resource # would fail when attempting to run `/etc/init.d/software-name status`. # In order to provide a more useful approximation of what would happen in # a real chef run, we want to instead assume that the service was created # but isn't running. The logic would look like this: # # # Hypothetical service provider demonstrating why run assumption logic. # # This isn't the actual API, it just shows the logic. # class HypotheticalServiceProvider < Chef::Provider # # def load_current_resource # # Make sure we have the init script available: # if ::File.exist?("/etc/init.d/some-service" # # If the init script exists, proceed as normal: # status_cmd = shell_out("/etc/init.d/some-service status") # if status_cmd.success? # @current_resource.status(:running) # else # @current_resource.status(:stopped) # end # else # if whyrun_mode? # # If the init script is not available, and we're in why run mode, # # assume that some previous action would've created it: # log("warning: init script '/etc/init.d/some-service' is not available") # log("warning: assuming that the init script would have been created, assuming the state of 'some-service' is 'stopped'") # @current_resource.status(:stopped) # else # raise "expected init script /etc/init.d/some-service doesn't exist" # end # end # end # # end # # In short, the code above does the following: # * runs a test to determine if a requirement is met: # `::File.exist?("/etc/init.d/some-service"` # * raises an error if the requirement is not met, and we're not in why # run mode. # * if we *are* in why run mode, print a message explaining the # situation, and run some code that makes an assumption about what the # state of the system would be. In this case, we also skip the normal # `load_current_resource` logic # * when the requirement *is* met, we run the normal `load_current_resource` # logic # # ResourceRequirements encapsulates the above logic in a more declarative API. # # === Examples # Assertions and assumptions should be created through the WhyRun#assert # method, which gets mixed in to providers. See that method's # documentation for examples. class ResourceRequirements # Implements the logic for a single assertion/assumption. See the # documentation for ResourceRequirements for full discussion. class Assertion class AssertionFailure < RuntimeError end def initialize @block_action = false @assertion_proc = nil @failure_message = nil @whyrun_message = nil @resource_modifier = nil @assertion_failed = false @exception_type = AssertionFailure end # Defines the code block that determines if a requirement is met. The # block should return a truthy value to indicate that the requirement # is met, and a falsey value if the requirement is not met. # # in a provider: # assert(:some_action) do |a| # # This provider requires the file /tmp/foo to exist: # a.assertion { ::File.exist?("/tmp/foo") } # end def assertion(&assertion_proc) @assertion_proc = assertion_proc end # Defines the failure message, and optionally the Exception class to # use when a requirement is not met. It works like `raise`: # # in a provider: # assert(:some_action) do |a| # # This example shows usage with 1 or 2 args by calling #failure_message twice. # # In practice you should only call this once per Assertion. # # # Set the Exception class explicitly # a.failure_message(Chef::Exceptions::MissingRequiredFile, "File /tmp/foo doesn't exist") # # Fallback to the default error class (AssertionFailure) # a.failure_message("File /tmp/foo" doesn't exist") # end def failure_message(*args) case args.size when 1 @failure_message = args[0] when 2 @exception_type, @failure_message = args[0], args[1] else raise ArgumentError, "#{self.class}#failure_message takes 1 or 2 arguments, you gave #{args.inspect}" end end # Defines a message and optionally provides a code block to execute # when the requirement is not met and Chef is executing in why run # mode # # If no failure_message is provided (above), then execution # will be allowed to continue in both whyrun and non-whyrun # mode # # With a service resource that requires /etc/init.d/service-name to exist: # # in a provider # assert(:start, :restart) do |a| # a.assertion { ::File.exist?("/etc/init.d/service-name") } # a.whyrun("Init script '/etc/init.d/service-name' doesn't exist, assuming a prior action would have created it.") do # # blindly assume that the service exists but is stopped in why run mode: # @new_resource.status(:stopped) # end # end def whyrun(message, &resource_modifier) @whyrun_message = message @resource_modifier = resource_modifier end # Prevents associated actions from being invoked in whyrun mode. # This will also stop further processing of assertions for a given action. # # An example from the template provider: if the source template doesn't exist # we can't parse it in the action_create block of template - something that we do # even in whyrun mode. Because the source template may have been created in an earlier # step, we still want to keep going in whyrun mode. # # assert(:create, :create_if_missing) do |a| # a.assertion { File::exists?(@new_resource.source) } # a.whyrun "Template source file does not exist, assuming it would have been created." # a.block_action! # end # def block_action! @block_action = true end def block_action? @block_action end def assertion_failed? @assertion_failed end # Runs the assertion/assumption logic. Will raise an Exception of the # type specified in #failure_message (or AssertionFailure by default) # if the requirement is not met and Chef is not running in why run # mode. An exception will also be raised if running in why run mode # and no why run message or block has been declared. def run(action, events, resource) if !@assertion_proc || !@assertion_proc.call @assertion_failed = true if Chef::Config[:why_run] && @whyrun_message events.provider_requirement_failed(action, resource, @exception_type, @failure_message) events.whyrun_assumption(action, resource, @whyrun_message) if @whyrun_message @resource_modifier.call if @resource_modifier else if @failure_message events.provider_requirement_failed(action, resource, @exception_type, @failure_message) raise @exception_type, @failure_message end end end end end def initialize(resource, run_context) @resource, @run_context = resource, run_context @assertions = Hash.new {|h,k| h[k] = [] } @blocked_actions = [] end def events @run_context.events end # Check to see if a given action is blocked by a failed assertion # # Takes the action name to be verified. def action_blocked?(action) @blocked_actions.include?(action) end # Define a new Assertion. # # Takes a list of action names for which the assertion should be made. # ==== Examples: # A File provider that requires the parent directory to exist: # # assert(:create, :create_if_missing) do |a| # parent_dir = File.basename(@new_resource.path) # a.assertion { ::File.directory?(parent_dir) } # a.failure_message(Exceptions::ParentDirectoryDoesNotExist, # "Can't create file #{@new_resource.path}: parent directory #{parent_dir} doesn't exist") # a.why_run("assuming parent directory #{parent_dir} would have been previously created" # end # # A service provider that requires the init script to exist: # # assert(:start, :restart) do |a| # a.assertion { ::File.exist?(@new_resource.init_script) } # a.failure_message(Exceptions::MissingInitScript, # "Can't check status of #{@new_resource}: init script #{@new_resource.init_script} is missing") # a.why_run("Assuming init script would have been created and service is stopped") do # @current_resource.status(:stopped) # end # end # # A File provider that will error out if you don't have permissions do # delete the file, *even in why run mode*: # # assert(:delete) do |a| # a.assertion { ::File.writable?(@new_resource.path) } # a.failure_message(Exceptions::InsufficientPrivileges, # "You don't have sufficient privileges to delete #{@new_resource.path}") # end # # A Template provider that will prevent action execution but continue the run in # whyrun mode if the template source is not available. # assert(:create, :create_if_missing) do |a| # a.assertion { File::exist?(@new_resource.source) } # a.failure_message Chef::Exceptions::TemplateError, "Template #{@new_resource.source} could not be found exist." # a.whyrun "Template source #{@new_resource.source} does not exist. Assuming it would have been created." # a.block_action! # end # # assert(:delete) do |a| # a.assertion { ::File.writable?(@new_resource.path) } # a.failure_message(Exceptions::InsufficientPrivileges, # "You don't have sufficient privileges to delete #{@new_resource.path}") # end def assert(*actions) assertion = Assertion.new yield assertion actions.each {|action| @assertions[action] << assertion } end # Run the assertion and assumption logic. def run(action) @assertions[action.to_sym].each do |a| a.run(action, events, @resource) if a.assertion_failed? and a.block_action? @blocked_actions << action return end end end end end end end chef-12.3.0/lib/chef/mixin/enforce_ownership_and_permissions.rb0000644000004100000410000000233312520074675024663 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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 'chef/file_access_control' class Chef module Mixin module EnforceOwnershipAndPermissions def access_controls @access_controls ||= Chef::FileAccessControl.new(current_resource, new_resource, self) end # will set the proper user, group and # permissions using a platform specific # version of Chef::FileAccessControl def enforce_ownership_and_permissions access_controls.set_all new_resource.updated_by_last_action(true) if access_controls.modified? end end end end chef-12.3.0/lib/chef/application.rb0000644000004100000410000003447612520074675017063 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Mark Mzyk (mmzyk@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 'pp' require 'uri' require 'socket' require 'chef/config' require 'chef/config_fetcher' require 'chef/exceptions' require 'chef/local_mode' require 'chef/log' require 'chef/platform' require 'mixlib/cli' require 'tmpdir' require 'rbconfig' class Chef class Application include Mixlib::CLI def initialize super @chef_client = nil @chef_client_json = nil # Always switch to a readable directory. Keeps subsequent Dir.chdir() {} # from failing due to permissions when launched as a less privileged user. end # Reconfigure the application. You'll want to override and super this method. def reconfigure configure_chef configure_logging configure_proxy_environment_variables configure_encoding emit_warnings end # Get this party started def run setup_signal_handlers reconfigure setup_application run_application end def setup_signal_handlers trap("INT") do Chef::Application.fatal!("SIGINT received, stopping", 2) end trap("TERM") do Chef::Application.fatal!("SIGTERM received, stopping", 3) end unless Chef::Platform.windows? trap("QUIT") do Chef::Log.info("SIGQUIT received, call stack:\n " + caller.join("\n ")) end trap("HUP") do Chef::Log.info("SIGHUP received, reconfiguring") reconfigure end end end # Parse configuration (options and config file) def configure_chef parse_options load_config_file end # Parse the config file def load_config_file config_fetcher = Chef::ConfigFetcher.new(config[:config_file]) if config[:config_file].nil? Chef::Log.warn("No config file found or specified on command line, using command line options.") elsif config_fetcher.config_missing? pp config_missing: true Chef::Log.warn("*****************************************") Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.") Chef::Log.warn("*****************************************") else config_content = config_fetcher.read_config apply_config(config_content, config[:config_file]) end Chef::Config.merge!(config) end def set_specific_recipes Chef::Config[:specific_recipes] = cli_arguments.map { |file| File.expand_path(file) } if cli_arguments.respond_to?(:map) end # Initialize and configure the logger. # === Loggers and Formatters # In Chef 10.x and previous, the Logger was the primary/only way that Chef # communicated information to the user. In Chef 10.14, a new system, "output # formatters" was added, and in Chef 11.0+ it is the default when running # chef in a console (detected by `STDOUT.tty?`). Because output formatters # are more complex than the logger system and users have less experience with # them, the config option `force_logger` is provided to restore the Chef 10.x # behavior. # # Conversely, for users who want formatter output even when chef is running # unattended, the `force_formatter` option is provided. # # === Auto Log Level # When `log_level` is set to `:auto` (default), the log level will be `:warn` # when the primary output mode is an output formatter (see # +using_output_formatter?+) and `:info` otherwise. # # === Automatic STDOUT Logging # When `force_logger` is configured (e.g., Chef 10 mode), a second logger # with output on STDOUT is added when running in a console (STDOUT is a tty) # and the configured log_location isn't STDOUT. This accounts for the case # 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(MonoLogger.new(Chef::Config[:log_location])) if want_additional_logger? configure_stdout_logger end Chef::Log.level = resolve_log_level rescue StandardError => error Chef::Log.fatal("Failed to open or create log file at #{Chef::Config[:log_location]}: #{error.class} (#{error.message})") Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", 2) end def configure_stdout_logger stdout_logger = MonoLogger.new(STDOUT) 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 # Configure and set any proxy environment variables according to the config. def configure_proxy_environment_variables configure_http_proxy configure_https_proxy configure_ftp_proxy configure_no_proxy end # Sets the default external encoding to UTF-8 (users can change this, but they shouldn't) def configure_encoding Encoding.default_external = Chef::Config[:ruby_encoding] end # Called prior to starting the application, by the run method def setup_application raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application" end # Actually run the application def run_application raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application" end # Initializes Chef::Client instance and runs it def run_chef_client(specific_recipes = []) unless specific_recipes.respond_to?(:size) raise ArgumentError, 'received non-Array like specific_recipes argument' end Chef::LocalMode.with_server_connectivity do override_runlist = config[:override_runlist] override_runlist ||= [] if specific_recipes.size > 0 @chef_client = Chef::Client.new( @chef_client_json, override_runlist: override_runlist, specific_recipes: specific_recipes, runlist: config[:runlist] ) @chef_client_json = nil if can_fork? fork_chef_client # allowed to run client in forked process else # Unforked interval runs are disabled, so this runs chef-client # once and then exits. If TERM signal is received, will "ignore" # the signal to finish converge. run_with_graceful_exit_option end @chef_client = nil end end private def can_fork? # win32-process gem exposes some form of :fork for Process # class. So we are separately ensuring that the platform we're # running on is not windows before forking. Chef::Config[:client_fork] && Process.respond_to?(:fork) && !Chef::Platform.windows? end # Run chef-client once and then exit. If TERM signal is received, ignores the # signal to finish the converge and exists. def run_with_graceful_exit_option # Override the TERM signal. trap('TERM') do Chef::Log.debug("SIGTERM received during converge," + " finishing converge to exit normally (send SIGINT to terminate immediately)") end @chef_client.run true end def fork_chef_client Chef::Log.info "Forking chef instance to converge..." pid = fork do # Want to allow forked processes to finish converging when # TERM singal is received (exit gracefully) trap('TERM') do Chef::Log.debug("SIGTERM received during converge," + " finishing converge to exit normally (send SIGINT to terminate immediately)") end client_solo = Chef::Config[:solo] ? "chef-solo" : "chef-client" $0 = "#{client_solo} worker: ppid=#{Process.ppid};start=#{Time.new.strftime("%R:%S")};" begin Chef::Log.debug "Forked instance now converging" @chef_client.run rescue Exception => e Chef::Log.error(e.to_s) exit 1 else exit 0 end end Chef::Log.debug "Fork successful. Waiting for new chef pid: #{pid}" result = Process.waitpid2(pid) handle_child_exit(result) Chef::Log.debug "Forked instance successfully reaped (pid: #{pid})" true end def handle_child_exit(pid_and_status) status = pid_and_status[1] return true if status.success? message = if status.signaled? "Chef run process terminated by signal #{status.termsig} (#{Signal.list.invert[status.termsig]})" else "Chef run process exited unsuccessfully (exit code #{status.exitstatus})" end raise Exceptions::ChildConvergeError, message end def apply_config(config_content, config_file_path) Chef::Config.from_string(config_content, config_file_path) rescue Exception => error Chef::Log.fatal("Configuration error #{error.class}: #{error.message}") filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/) filtered_trace.each {|line| Chef::Log.fatal(" " + line )} Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", 2) end # Set ENV['http_proxy'] def configure_http_proxy if http_proxy = Chef::Config[:http_proxy] http_proxy_string = configure_proxy("http", http_proxy, Chef::Config[:http_proxy_user], Chef::Config[:http_proxy_pass]) env['http_proxy'] = http_proxy_string unless env['http_proxy'] env['HTTP_PROXY'] = http_proxy_string unless env['HTTP_PROXY'] end end # Set ENV['https_proxy'] def configure_https_proxy if https_proxy = Chef::Config[:https_proxy] https_proxy_string = configure_proxy("https", https_proxy, Chef::Config[:https_proxy_user], Chef::Config[:https_proxy_pass]) env['https_proxy'] = https_proxy_string unless env['https_proxy'] env['HTTPS_PROXY'] = https_proxy_string unless env['HTTPS_PROXY'] end end # Set ENV['ftp_proxy'] def configure_ftp_proxy if ftp_proxy = Chef::Config[:ftp_proxy] ftp_proxy_string = configure_proxy("ftp", ftp_proxy, Chef::Config[:ftp_proxy_user], Chef::Config[:ftp_proxy_pass]) env['ftp_proxy'] = ftp_proxy_string unless env['ftp_proxy'] env['FTP_PROXY'] = ftp_proxy_string unless env['FTP_PROXY'] end end # Set ENV['no_proxy'] def configure_no_proxy if Chef::Config[:no_proxy] env['no_proxy'] = Chef::Config[:no_proxy] unless env['no_proxy'] env['NO_PROXY'] = Chef::Config[:no_proxy] unless env['NO_PROXY'] end end # Builds a proxy uri. Examples: # http://username:password@hostname:port # https://username@hostname:port # ftp://hostname:port # when # scheme = "http", "https", or "ftp" # hostport = hostname:port # user = username # pass = password def configure_proxy(scheme, path, user, pass) begin path = "#{scheme}://#{path}" unless path.include?('://') # URI.split returns the following parts: # [scheme, userinfo, host, port, registry, path, opaque, query, fragment] parts = URI.split(URI.encode(path)) # URI::Generic.build requires an integer for the port, but URI::split gives # returns a string for the port. parts[3] = parts[3].to_i if parts[3] if user userinfo = URI.encode(URI.encode(user), '@:') if pass userinfo << ":#{URI.encode(URI.encode(pass), '@:')}" end parts[1] = userinfo end return URI::Generic.build(parts).to_s rescue URI::Error => e # URI::Error messages generally include the offending string. Including a message # for which proxy config item has the issue should help deduce the issue when # the URI::Error message is vague. raise Chef::Exceptions::BadProxyURI, "Cannot configure #{scheme} proxy. Does not comply with URI scheme. #{e.message}" end end # This is a hook for testing def env ENV end def emit_warnings if Chef::Config[:chef_gem_compile_time] Chef::Log.deprecation "setting chef_gem_compile_time to true is deprecated" end end class << self def debug_stacktrace(e) message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" chef_stacktrace_out = "Generated at #{Time.now.to_s}\n" chef_stacktrace_out += message Chef::FileCache.store("chef-stacktrace.out", chef_stacktrace_out) Chef::Log.fatal("Stacktrace dumped to #{Chef::FileCache.load("chef-stacktrace.out", false)}") Chef::Log.debug(message) true end # Log a fatal error message to both STDERR and the Logger, exit the application def fatal!(msg, err = -1) Chef::Log.fatal(msg) Process.exit err end def exit!(msg, err = -1) Chef::Log.debug(msg) Process.exit err end end end end chef-12.3.0/lib/chef/cookbook_version.rb0000644000004100000410000005215612520074675020126 0ustar www-datawww-data# Author:: Adam Jacob () # Author:: Nuo Yan () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Author:: Seth Falcon () # Author:: Daniel DeLeo () # Copyright:: Copyright 2008-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 'chef/log' require 'chef/cookbook/file_vendor' require 'chef/cookbook/metadata' require 'chef/version_class' require 'chef/digester' require 'chef/cookbook_manifest' class Chef # == Chef::CookbookVersion # CookbookVersion is a model object encapsulating the data about a Chef # cookbook. Chef supports maintaining multiple versions of a cookbook on a # single server; each version is represented by a distinct instance of this # class. class CookbookVersion include Comparable COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ] attr_accessor :root_paths attr_accessor :definition_filenames attr_accessor :template_filenames attr_accessor :file_filenames attr_accessor :library_filenames attr_accessor :resource_filenames attr_accessor :provider_filenames attr_accessor :root_filenames attr_accessor :name attr_accessor :metadata_filenames def status=(new_status) Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed") @status = new_status end def status Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed") @status end # A Chef::Cookbook::Metadata object. It has a setter that fixes up the # metadata to add descriptions of the recipes contained in this # CookbookVersion. attr_reader :metadata # attribute_filenames also has a setter that has non-default # functionality. attr_reader :attribute_filenames # recipe_filenames also has a setter that has non-default # functionality. attr_reader :recipe_filenames attr_reader :recipe_filenames_by_name attr_reader :attribute_filenames_by_short_filename attr_accessor :chef_server_rest # The `identifier` field is used for cookbook_artifacts, which are # organized on the chef server according to their content. If the # policy_mode option to CookbookManifest is set to true it will include # this field in the manifest Hash and in the upload URL. # # This field may be removed or have different behavior in the future, don't # use it in 3rd party code. # @api private attr_accessor :identifier # The first root path is the primary cookbook dir, from which metadata is loaded def root_dir root_paths[0] end # This is the one and only method that knows how cookbook files' # checksums are generated. def self.checksum_cookbook_file(filepath) Chef::Digester.generate_md5_checksum_for_file(filepath) rescue Errno::ENOENT Chef::Log.debug("File #{filepath} does not exist, so there is no checksum to generate") nil end def self.cache Chef::FileCache end # Creates a new Chef::CookbookVersion object. # # === Returns # object:: Duh. :) def initialize(name, *root_paths, chef_server_rest: nil) @name = name @root_paths = root_paths @frozen = false @attribute_filenames = Array.new @definition_filenames = Array.new @template_filenames = Array.new @file_filenames = Array.new @recipe_filenames = Array.new @recipe_filenames_by_name = Hash.new @library_filenames = Array.new @resource_filenames = Array.new @provider_filenames = Array.new @metadata_filenames = Array.new @root_filenames = Array.new # deprecated @status = :ready @file_vendor = nil @metadata = Chef::Cookbook::Metadata.new @chef_server_rest = chef_server_rest end def version metadata.version end # Indicates if this version is frozen or not. Freezing a coobkook version # indicates that a new cookbook with the same name and version number # shoule def frozen_version? @frozen end def freeze_version @frozen = true end def version=(new_version) cookbook_manifest.reset! metadata.version(new_version) end def full_name "#{name}-#{version}" end def attribute_filenames=(*filenames) @attribute_filenames = filenames.flatten @attribute_filenames_by_short_filename = filenames_by_name(attribute_filenames) attribute_filenames end def metadata=(metadata) @metadata = metadata @metadata.recipes_from_cookbook_version(self) @metadata end ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]## alias :attribute_files :attribute_filenames alias :attribute_files= :attribute_filenames= def manifest cookbook_manifest.manifest end # Returns a hash of checksums to either nil or the on disk path (which is # done by generate_manifest). def checksums cookbook_manifest.checksums end def manifest_records_by_path cookbook_manifest.manifest_records_by_path end def manifest=(new_manifest) cookbook_manifest.update_from(new_manifest) end # Return recipe names in the form of cookbook_name::recipe_name def fully_qualified_recipe_names results = Array.new recipe_filenames_by_name.each_key do |rname| results << "#{name}::#{rname}" end results end def recipe_filenames=(*filenames) @recipe_filenames = filenames.flatten @recipe_filenames_by_name = filenames_by_name(recipe_filenames) recipe_filenames end ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]## alias :recipe_files :recipe_filenames alias :recipe_files= :recipe_filenames= # called from DSL def load_recipe(recipe_name, run_context) unless recipe_filenames_by_name.has_key?(recipe_name) raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}" end Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}") recipe = Chef::Recipe.new(name, recipe_name, run_context) recipe_filename = recipe_filenames_by_name[recipe_name] unless recipe_filename raise Chef::Exceptions::RecipeNotFound, "could not find #{recipe_name} files for cookbook #{name}" end recipe.from_file(recipe_filename) recipe end def segment_filenames(segment) unless COOKBOOK_SEGMENTS.include?(segment) raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}" end case segment.to_sym when :resources @resource_filenames when :providers @provider_filenames when :recipes @recipe_filenames when :libraries @library_filenames when :definitions @definition_filenames when :attributes @attribute_filenames when :files @file_filenames when :templates @template_filenames when :root_files @root_filenames end end def replace_segment_filenames(segment, filenames) case segment.to_sym when :recipes self.recipe_filenames = filenames when :attributes self.attribute_filenames = filenames else segment_filenames(segment).replace(filenames) end end # Query whether a template file +template_filename+ is available. File # specificity for the given +node+ is obeyed in the lookup. def has_template_for_node?(node, template_filename) !!find_preferred_manifest_record(node, :templates, template_filename) end # Query whether a cookbook_file file +cookbook_filename+ is available. File # specificity for the given +node+ is obeyed in the lookup. def has_cookbook_file_for_node?(node, cookbook_filename) !!find_preferred_manifest_record(node, :files, cookbook_filename) end # Determine the most specific manifest record for the given # segment/filename, given information in the node. Throws # FileNotFound if there is no such segment and filename in the # manifest. # # A manifest record is a Mash that follows the following form: # { # :name => "example.rb", # :path => "files/default/example.rb", # :specificity => "default", # :checksum => "1234" # } def preferred_manifest_record(node, segment, filename) found_pref = find_preferred_manifest_record(node, segment, filename) if found_pref manifest_records_by_path[found_pref] else if segment == :files || segment == :templates error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n" error_locations = if filename.is_a?(Array) filename.map{|name| " #{File.join(segment.to_s, name)}"} else [ " #{segment}/#{node[:platform]}-#{node[:platform_version]}/#{filename}", " #{segment}/#{node[:platform]}/#{filename}", " #{segment}/default/#{filename}", " #{segment}/#{filename}", ] end error_message << error_locations.join("\n") existing_files = segment_filenames(segment) # Strip the root_dir prefix off all files for readability existing_files.map!{|path| path[root_dir.length+1..-1]} if root_dir # Show the files that the cookbook does have. If the user made a typo, # hopefully they'll see it here. unless existing_files.empty? error_message << "\n\nThis cookbook _does_ contain: ['#{existing_files.join("','")}']" end raise Chef::Exceptions::FileNotFound, error_message else raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}" end end end def preferred_filename_on_disk_location(node, segment, filename, current_filepath=nil) manifest_record = preferred_manifest_record(node, segment, filename) if current_filepath && (manifest_record['checksum'] == self.class.checksum_cookbook_file(current_filepath)) nil else file_vendor.get_filename(manifest_record['path']) end end def relative_filenames_in_preferred_directory(node, segment, dirname) preferences = preferences_for_path(node, segment, dirname) filenames_by_pref = Hash.new preferences.each { |pref| filenames_by_pref[pref] = Array.new } manifest[segment].each do |manifest_record| manifest_record_path = manifest_record[:path] # find the NON SPECIFIC filenames, but prefer them by filespecificity. # For example, if we have a file: # 'files/default/somedir/somefile.conf' we only keep # 'somedir/somefile.conf'. If there is also # 'files/$hostspecific/somedir/otherfiles' that matches the requested # hostname specificity, that directory will win, as it is more specific. # # This is clearly ugly b/c the use case is for remote directory, where # we're just going to make cookbook_files out of these and make the # cookbook find them by filespecificity again. but it's the shortest # path to "success" for now. if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/ specificity_dirname = $1 non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1] # Record the specificity_dirname only if it's in the list of # valid preferences if filenames_by_pref[specificity_dirname] filenames_by_pref[specificity_dirname] << non_specific_path end end end best_pref = preferences.find { |pref| !filenames_by_pref[pref].empty? } raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/default/#{dirname}" unless best_pref filenames_by_pref[best_pref] end # Determine the manifest records from the most specific directory # for the given node. See #preferred_manifest_record for a # description of entries of the returned Array. def preferred_manifest_records_for_directory(node, segment, dirname) preferences = preferences_for_path(node, segment, dirname) records_by_pref = Hash.new preferences.each { |pref| records_by_pref[pref] = Array.new } manifest[segment].each do |manifest_record| manifest_record_path = manifest_record[:path] # extract the preference part from the path. if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/ # Note the specificy_dirname includes the segment and # dirname argument as above, which is what # preferences_for_path returns. It could be # "files/ubuntu-9.10/dirname", for example. specificity_dirname = $1 # Record the specificity_dirname only if it's in the list of # valid preferences if records_by_pref[specificity_dirname] records_by_pref[specificity_dirname] << manifest_record end end end best_pref = preferences.find { |pref| !records_by_pref[pref].empty? } raise Chef::Exceptions::FileNotFound, "cookbook #{name} (#{version}) has no directory #{segment}/default/#{dirname}" unless best_pref records_by_pref[best_pref] end # Given a node, segment and path (filename or directory name), # return the priority-ordered list of preference locations to # look. def preferences_for_path(node, segment, path) # only files and templates can be platform-specific if segment.to_sym == :files || segment.to_sym == :templates relative_search_path = if path.is_a?(Array) path else begin platform, version = Chef::Platform.find_platform_and_version(node) rescue ArgumentError => e # Skip platform/version if they were not found by find_platform_and_version if e.message =~ /Cannot find a (?:platform|version)/ platform = "/unknown_platform/" version = "/unknown_platform_version/" else raise end end fqdn = node[:fqdn] # Break version into components, eg: "5.7.1" => [ "5.7.1", "5.7", "5" ] search_versions = [] parts = version.to_s.split('.') parts.size.times do search_versions << parts.join('.') parts.pop end # Most specific to least specific places to find the path search_path = [ File.join("host-#{fqdn}", path) ] search_versions.each do |v| search_path << File.join("#{platform}-#{v}", path) end search_path << File.join(platform.to_s, path) search_path << File.join("default", path) search_path << path search_path end relative_search_path.map {|relative_path| File.join(segment.to_s, relative_path)} else [File.join(segment, path)] end end private :preferences_for_path def self.json_create(o) cookbook_version = new(o["cookbook_name"]) # We want the Chef::Cookbook::Metadata object to always be inflated cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"]) 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"] = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(cookbook_version.metadata)) cookbook_version.freeze_version if o["frozen?"] cookbook_version end def self.from_cb_artifact_data(o) cookbook_version = new(o["name"]) # We want the Chef::Cookbook::Metadata object to always be inflated cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"]) cookbook_version.manifest = o cookbook_version.identifier = o["identifier"] cookbook_version end # @deprecated This method was used by the Ruby Chef Server and is no longer # needed. There is no replacement. def generate_manifest_with_urls(&url_generator) Chef::Log.deprecation("Deprecated method #generate_manifest_with_urls called from #{caller(1).first}") rendered_manifest = manifest.dup COOKBOOK_SEGMENTS.each do |segment| if rendered_manifest.has_key?(segment) rendered_manifest[segment].each do |manifest_record| url_options = { :cookbook_name => name.to_s, :cookbook_version => version, :checksum => manifest_record["checksum"] } manifest_record["url"] = url_generator.call(url_options) end end end rendered_manifest end def to_hash # TODO: this should become deprecated when the API for CookbookManifest becomes stable cookbook_manifest.to_hash end def to_json(*a) # TODO: this should become deprecated when the API for CookbookManifest becomes stable cookbook_manifest.to_json end def metadata_json_file File.join(root_paths[0], "metadata.json") end def metadata_rb_file File.join(root_paths[0], "metadata.rb") end def reload_metadata! if File.exists?(metadata_json_file) metadata.from_json(IO.read(metadata_json_file)) end end ## # REST API ## def save_url # TODO: this should become deprecated when the API for CookbookManifest becomes stable cookbook_manifest.save_url end def force_save_url # TODO: this should become deprecated when the API for CookbookManifest becomes stable cookbook_manifest.force_save_url end def chef_server_rest @chef_server_rest ||= self.chef_server_rest end def self.chef_server_rest Chef::REST.new(Chef::Config[:chef_server_url]) end def destroy chef_server_rest.delete_rest("cookbooks/#{name}/#{version}") self end def self.load(name, version="_latest") version = "_latest" if version == "latest" chef_server_rest.get_rest("cookbooks/#{name}/#{version}") end # The API returns only a single version of each cookbook in the result from the cookbooks method def self.list chef_server_rest.get_rest('cookbooks') end # Alias latest_cookbooks as list class << self alias :latest_cookbooks :list end def self.list_all_versions chef_server_rest.get_rest('cookbooks?num_versions=all') end ## # Given a +cookbook_name+, get a list of all versions that exist on the # server. # ===Returns # [String]:: Array of cookbook versions, which are strings like 'x.y.z' # nil:: if the cookbook doesn't exist. an error will also be logged. def self.available_versions(cookbook_name) chef_server_rest.get_rest("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map do |cb| cb["version"] end rescue Net::HTTPServerException => e if e.to_s =~ /^404/ Chef::Log.error("Cannot find a cookbook named #{cookbook_name}") nil else raise end end def <=>(o) raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name # FIXME: can we change the interface to the Metadata class such # that metadata.version returns a Chef::Version instance instead # of a string? Chef::Version.new(self.version) <=> Chef::Version.new(o.version) end private def cookbook_manifest @cookbook_manifest ||= CookbookManifest.new(self) end def find_preferred_manifest_record(node, segment, filename) preferences = preferences_for_path(node, segment, filename) # in order of prefernce, look for the filename in the manifest preferences.find {|preferred_filename| manifest_records_by_path[preferred_filename] } end # For each filename, produce a mapping of base filename (i.e. recipe name # or attribute file) to on disk location def filenames_by_name(filenames) filenames.select{|filename| filename =~ /\.rb$/}.inject({}){|memo, filename| memo[File.basename(filename, '.rb')] = filename ; memo } end def file_vendor unless @file_vendor @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest) end @file_vendor end end end chef-12.3.0/lib/chef/chef_fs/0000755000004100000410000000000012520074675015612 5ustar www-datawww-datachef-12.3.0/lib/chef/chef_fs/file_pattern.rb0000644000004100000410000002630112520074675020615 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs' require 'chef/chef_fs/path_utils' class Chef module ChefFS # # Represents a glob pattern. This class is designed so that it can # match arbitrary strings, and tell you about partial matches. # # Examples: # * a*z # - Matches abcz # - Does not match ab/cd/ez # - Does not match xabcz # * a**z # - Matches abcz # - Matches ab/cd/ez # # Special characters supported: # * / (and \\ on Windows) - directory separators # * \* - match zero or more characters (but not directory separators) # * \*\* - match zero or more characters, including directory separators # * ? - match exactly one character (not a directory separator) # Only on Unix: # * [abc0-9] - match one of the included characters # * \\ - escape character: match the given character # class FilePattern # Initialize a new FilePattern with the pattern string. # # Raises +ArgumentError+ if empty file pattern is specified def initialize(pattern) @pattern = pattern end # The pattern string. attr_reader :pattern # Reports whether this pattern could match children of path. # If the pattern doesn't match the path up to this point or # if it matches and doesn't allow further children, this will # return false. # # ==== Attributes # # * +path+ - a path to check # # ==== Examples # # abc/def.could_match_children?('abc') == true # abc.could_match_children?('abc') == false # abc/def.could_match_children?('x') == false # a**z.could_match_children?('ab/cd') == true def could_match_children?(path) return false if path == '' # Empty string is not a path argument_is_absolute = !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/) return false if is_absolute != argument_is_absolute path = path[1,path.length-1] if argument_is_absolute path_parts = Chef::ChefFS::PathUtils::split(path) # If the pattern is shorter than the path (or same size), children will be larger than the pattern, and will not match. return false if regexp_parts.length <= path_parts.length && !has_double_star # If the path doesn't match up to this point, children won't match either. return false if path_parts.zip(regexp_parts).any? { |part,regexp| !regexp.nil? && !regexp.match(part) } # Otherwise, it's possible we could match: the path matches to this point, and the pattern is longer than the path. # TODO There is one edge case where the double star comes after some characters like abc**def--we could check whether the next # bit of path starts with abc in that case. return true end # Returns the immediate child of a path that would be matched # if this FilePattern was applied. If more than one child # could match, this method returns nil. # # ==== Attributes # # * +path+ - The path to look for an exact child name under. # # ==== Returns # # The next directory in the pattern under the given path. # If the directory part could match more than one child, it # returns +nil+. # # ==== Examples # # abc/def.exact_child_name_under('abc') == 'def' # abc/def/ghi.exact_child_name_under('abc') == 'def' # abc/*/ghi.exact_child_name_under('abc') == nil # abc/*/ghi.exact_child_name_under('abc/def') == 'ghi' # abc/**/ghi.exact_child_name_under('abc/def') == nil # # This method assumes +could_match_children?(path)+ is +true+. def exact_child_name_under(path) path = path[1,path.length-1] if !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/) dirs_in_path = Chef::ChefFS::PathUtils::split(path).length return nil if exact_parts.length <= dirs_in_path return exact_parts[dirs_in_path] end # If this pattern represents an exact path, returns the exact path. # # abc/def.exact_path == 'abc/def' # abc/*def.exact_path == 'abc/def' # abc/x\\yz.exact_path == 'abc/xyz' def exact_path return nil if has_double_star || exact_parts.any? { |part| part.nil? } result = Chef::ChefFS::PathUtils::join(*exact_parts) is_absolute ? Chef::ChefFS::PathUtils::join('', result) : result end # Returns the normalized version of the pattern, with / as the directory # separator, and "." and ".." removed. # # This does not presently change things like \b to b, but in the future # it might. def normalized_pattern calculate @normalized_pattern end # Tell whether this pattern matches absolute, or relative paths def is_absolute calculate @is_absolute end # Returns true+ if this pattern matches the path, false+ otherwise. # # abc/*/def.match?('abc/foo/def') == true # abc/*/def.match?('abc/foo') == false def match?(path) argument_is_absolute = !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/) return false if is_absolute != argument_is_absolute path = path[1,path.length-1] if argument_is_absolute !!regexp.match(path) end # Returns the string pattern def to_s pattern end # Given a relative file pattern and a directory, makes a new file pattern # starting with the directory. # # FilePattern.relative_to('/usr/local', 'bin/*grok') == FilePattern.new('/usr/local/bin/*grok') # # BUG: this does not support patterns starting with .. def self.relative_to(dir, pattern) return FilePattern.new(pattern) if pattern =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/ FilePattern.new(Chef::ChefFS::PathUtils::join(dir, pattern)) end private def regexp calculate @regexp end def regexp_parts calculate @regexp_parts end def exact_parts calculate @exact_parts end def has_double_star calculate @has_double_star end def calculate if !@regexp @is_absolute = !!(@pattern =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/) full_regexp_parts = [] normalized_parts = [] @regexp_parts = [] @exact_parts = [] @has_double_star = false Chef::ChefFS::PathUtils::split(pattern).each do |part| regexp, exact, has_double_star = FilePattern::pattern_to_regexp(part) if has_double_star @has_double_star = true end # Skip // and /./ (pretend it's not there) if exact == '' || exact == '.' next end # Back up when you see .. (unless the prior part has ** in it, in which case .. must be preserved) if exact == '..' if @is_absolute && normalized_parts.length == 0 # If we are at the root, just pretend the .. isn't there next elsif normalized_parts.length > 0 regexp_prev, exact_prev, has_double_star_prev = FilePattern.pattern_to_regexp(normalized_parts[-1]) if has_double_star_prev raise ArgumentError, ".. overlapping a ** is unsupported" end full_regexp_parts.pop normalized_parts.pop if !@has_double_star @regexp_parts.pop @exact_parts.pop end next end end # Build up the regexp full_regexp_parts << regexp normalized_parts << part if !@has_double_star @regexp_parts << Regexp.new("^#{regexp}$") @exact_parts << exact end end @regexp = Regexp.new("^#{full_regexp_parts.join(Chef::ChefFS::PathUtils::regexp_path_separator)}$") @normalized_pattern = Chef::ChefFS::PathUtils.join(*normalized_parts) @normalized_pattern = Chef::ChefFS::PathUtils.join('', @normalized_pattern) if @is_absolute end end def self.pattern_special_characters if Chef::ChefFS::windows? @pattern_special_characters ||= /(\*\*|\*|\?|[\*\?\.\|\(\)\[\]\{\}\+\\\\\^\$])/ else # Unix also supports character regexes and backslashes @pattern_special_characters ||= /(\\.|\[[^\]]+\]|\*\*|\*|\?|[\*\?\.\|\(\)\[\]\{\}\+\\\\\^\$])/ end @pattern_special_characters end def self.regexp_escape_characters [ '[', '\\', '^', '$', '.', '|', '?', '*', '+', '(', ')', '{', '}' ] end def self.pattern_to_regexp(pattern) regexp = "" exact = "" has_double_star = false pattern.split(pattern_special_characters).each_with_index do |part, index| # Odd indexes from the split are symbols. Even are normal bits. if index % 2 == 0 exact << part if !exact.nil? regexp << part else case part # **, * and ? happen on both platforms. when '**' exact = nil has_double_star = true regexp << '.*' when '*' exact = nil regexp << '[^\/]*' when '?' exact = nil regexp << '.' else if part[0,1] == '\\' && part.length == 2 # backslash escapes are only supported on Unix, and are handled here by leaving the escape on (it means the same thing in a regex) exact << part[1,1] if !exact.nil? if regexp_escape_characters.include?(part[1,1]) regexp << part else regexp << part[1,1] end elsif part[0,1] == '[' && part.length > 1 # [...] happens only on Unix, and is handled here by *not* backslashing (it means the same thing in and out of regex) exact = nil regexp << part else exact += part if !exact.nil? regexp << "\\#{part}" end end end end [regexp, exact, has_double_star] end end end end chef-12.3.0/lib/chef/chef_fs/config.rb0000644000004100000410000002310212520074675017402 0ustar www-datawww-data# # Author:: John Keiser () # 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/log' require 'chef/chef_fs/path_utils' class Chef module ChefFS # # Helpers to take Chef::Config and create chef_fs and local_fs (ChefFS # objects representing the server and local repository, respectively). # class Config # Not all of our object types pluralize by adding an 's', so we map them # out here: INFLECTIONS = { "acls" => "acl", "clients" => "client", "cookbooks" => "cookbook", "containers" => "container", "data_bags" => "data_bag", "environments" => "environment", "groups" => "group", "nodes" => "node", "roles" => "role", "users" => "user", "policies" => "policy" } INFLECTIONS.each { |k,v| k.freeze; v.freeze } INFLECTIONS.freeze # # Create a new Config object which can produce a chef_fs and local_fs. # # ==== Arguments # # [chef_config] # A hash that looks suspiciously like +Chef::Config+. These hash keys # include: # # :chef_repo_path:: # The root where all local chef object data is stored. Mirrors # +Chef::Config.chef_repo_path+ # :cookbook_path, node_path, ...:: # Paths to cookbooks/, nodes/, data_bags/, etc. Mirrors # +Chef::Config.cookbook_path+, etc. Defaults to # +/cookbooks+, etc. # :repo_mode:: # The directory format on disk. 'everything', 'hosted_everything' and # 'static'. Default: autodetected based on whether the URL has # "/organizations/NAME." # :versioned_cookbooks:: # If true, the repository contains cookbooks with versions in their # name (apache2-1.0.0). If false, the repository just has one version # of each cookbook and the directory has the cookbook name (apache2). # Default: +false+ # :chef_server_url:: # The URL to the Chef server, e.g. https://api.opscode.com/organizations/foo. # Used as the server for the remote chef_fs, and to "guess" repo_mode # if not specified. # :node_name:: The username to authenticate to the Chef server with. # :client_key:: The private key for the user for authentication # :environment:: The environment in which you are presently working # :repo_mode:: # The repository mode, :hosted_everything, :everything or :static. # This determines the set of subdirectories the Chef server will offer # up. # :versioned_cookbooks:: Whether or not to include versions in cookbook names # # [cwd] # The current working directory to base relative Chef paths from. # Defaults to +Dir.pwd+. # # [options] # A hash of other, not-suspiciously-like-chef-config options: # :cookbook_version:: # When downloading cookbooks, download this cookbook version instead # of the latest. # # [ui] # The object to print output to, with "output", "warn" and "error" # (looks a little like a Chef::Knife::UI object, obtainable from # Chef::Knife.ui). # # ==== Example # # require 'chef/chef_fs/config' # config = Chef::ChefFS::Config.new # config.chef_fs.child('cookbooks').children.each do |cookbook| # puts "Cookbook on server: #{cookbook.name}" # end # config.local_fs.child('cookbooks').children.each do |cookbook| # puts "Local cookbook: #{cookbook.name}" # end # def initialize(chef_config = Chef::Config, cwd = Dir.pwd, options = {}, ui = nil) @chef_config = chef_config @cwd = cwd @cookbook_version = options[:cookbook_version] if @chef_config[:repo_mode] == 'everything' && is_hosted? && !ui.nil? ui.warn %Q{You have repo_mode set to 'everything', but your chef_server_url looks like it might be a hosted setup. If this is the case please use hosted_everything or allow repo_mode to default} end # Default to getting *everything* from the server. if !@chef_config[:repo_mode] if is_hosted? @chef_config[:repo_mode] = 'hosted_everything' else @chef_config[:repo_mode] = 'everything' end end end attr_reader :chef_config attr_reader :cwd attr_reader :cookbook_version def is_hosted? @chef_config[:chef_server_url] =~ /\/+organizations\/.+/ end def chef_fs @chef_fs ||= create_chef_fs end def create_chef_fs require 'chef/chef_fs/file_system/chef_server_root_dir' Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", @chef_config, :cookbook_version => @cookbook_version) end def local_fs @local_fs ||= create_local_fs end def create_local_fs require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir' Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths, Array(chef_config[:chef_repo_path]).flatten, @chef_config) end # Returns the given real path's location relative to the server root. # # If chef_repo is /home/jkeiser/chef_repo, # and pwd is /home/jkeiser/chef_repo/cookbooks, # server_path('blah') == '/cookbooks/blah' # server_path('../roles/blah.json') == '/roles/blah' # server_path('../../readme.txt') == nil # server_path('*/*ab*') == '/cookbooks/*/*ab*' # server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah' # server_path('/home/*/chef_repo/cookbooks/blah') == nil # # If there are multiple paths (cookbooks, roles, data bags, etc. can all # have separate paths), and cwd+the path reaches into one of them, we will # return a path relative to that. Otherwise we will return a path to # chef_repo. # # Globs are allowed as well, but globs outside server paths are NOT # (presently) supported. See above examples. TODO support that. # # If the path does not reach into ANY specified directory, nil is returned. def server_path(file_path) pwd = File.expand_path(Dir.pwd) absolute_pwd = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd)) # Check all object paths (cookbooks_dir, data_bags_dir, etc.) object_paths.each_pair do |name, paths| paths.each do |path| realest_path = Chef::ChefFS::PathUtils.realest_path(path) if PathUtils.descendant_of?(absolute_pwd, realest_path) relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_pwd, realest_path) return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}" end end end # Check chef_repo_path Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path| realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path) if absolute_pwd == realest_chef_repo_path return '/' end end nil end # The current directory, relative to server root def base_path @base_path ||= begin if @chef_config[:chef_repo_path] server_path(File.expand_path(@cwd)) else nil end end end # Print the given server path, relative to the current directory def format_path(entry) server_path = entry.path if base_path && server_path[0,base_path.length] == base_path if server_path == base_path return "." elsif server_path[base_path.length,1] == "/" return server_path[base_path.length + 1, server_path.length - base_path.length - 1] elsif base_path == "/" && server_path[0,1] == "/" return server_path[1, server_path.length - 1] end end server_path end private def object_paths @object_paths ||= begin result = {} case @chef_config[:repo_mode] when 'static' object_names = %w(cookbooks data_bags environments roles policies) when 'hosted_everything' object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles policies) else object_names = %w(clients cookbooks data_bags environments nodes roles users policies) end object_names.each do |object_name| # cookbooks -> cookbook_path singular_name = INFLECTIONS[object_name] or raise "Unknown object name #{object_name}" variable_name = "#{singular_name}_path" paths = Array(@chef_config[variable_name]).flatten result[object_name] = paths.map { |path| File.expand_path(path) } end result end end end end end chef-12.3.0/lib/chef/chef_fs/path_utils.rb0000644000004100000410000000661512520074675020323 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs' require 'pathname' class Chef module ChefFS class PathUtils # If you are in 'source', this is what you would have to type to reach 'dest' # relative_to('/a/b/c/d/e', '/a/b/x/y') == '../../c/d/e' # relative_to('/a/b', '/a/b') == '.' def self.relative_to(dest, source) # Skip past the common parts source_parts = Chef::ChefFS::PathUtils.split(source) dest_parts = Chef::ChefFS::PathUtils.split(dest) i = 0 until i >= source_parts.length || i >= dest_parts.length || source_parts[i] != dest_parts[i] i+=1 end # dot-dot up from 'source' to the common ancestor, then # descend to 'dest' from the common ancestor result = Chef::ChefFS::PathUtils.join(*(['..']*(source_parts.length-i) + dest_parts[i,dest.length-i])) result == '' ? '.' : result end def self.join(*parts) return "" if parts.length == 0 # Determine if it started with a slash absolute = parts[0].length == 0 || parts[0].length > 0 && parts[0] =~ /^#{regexp_path_separator}/ # Remove leading and trailing slashes from each part so that the join will work (and the slash at the end will go away) parts = parts.map { |part| part.gsub(/^\/|\/$/, "") } # Don't join empty bits result = parts.select { |part| part != "" }.join("/") # Put the / back on absolute ? "/#{result}" : result end def self.split(path) path.split(Regexp.new(regexp_path_separator)) end def self.regexp_path_separator Chef::ChefFS::windows? ? '[\/\\\\]' : '/' end # Given a path which may only be partly real (i.e. /x/y/z when only /x exists, # or /x/y/*/blah when /x/y/z/blah exists), call File.realpath on the biggest # part that actually exists. # # If /x is a symlink to /blarghle, and has no subdirectories, then: # PathUtils.realest_path('/x/y/z') == '/blarghle/y/z' # PathUtils.realest_path('/x/*/z') == '/blarghle/*/z' # PathUtils.realest_path('/*/y/z') == '/*/y/z' def self.realest_path(path) path = Pathname.new(path) begin path.realpath.to_s rescue Errno::ENOENT dirname = path.dirname if dirname PathUtils.join(realest_path(dirname), path.basename.to_s) else path.to_s end end end def self.descendant_of?(path, ancestor) path[0,ancestor.length] == ancestor && (ancestor.length == path.length || path[ancestor.length,1] =~ /#{PathUtils.regexp_path_separator}/) end def self.is_absolute?(path) path =~ /^#{regexp_path_separator}/ end end end end chef-12.3.0/lib/chef/chef_fs/parallelizer/0000755000004100000410000000000012520074675020300 5ustar www-datawww-datachef-12.3.0/lib/chef/chef_fs/parallelizer/flatten_enumerable.rb0000644000004100000410000000135712520074675024467 0ustar www-datawww-dataclass Chef module ChefFS class Parallelizer class FlattenEnumerable include Enumerable def initialize(enum, levels = nil) @enum = enum @levels = levels end attr_reader :enum attr_reader :levels def each(&block) enum.each do |value| flatten(value, levels, &block) end end private def flatten(value, levels, &block) if levels != 0 && value.respond_to?(:each) && !value.is_a?(String) value.each do |child| flatten(child, levels.nil? ? levels : levels-1, &block) end else block.call(value) end end end end end end chef-12.3.0/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb0000644000004100000410000002207512520074675024626 0ustar www-datawww-datarequire 'chef/chef_fs/parallelizer/flatten_enumerable' class Chef module ChefFS class Parallelizer class ParallelEnumerable include Enumerable # options: # :ordered [true|false] - whether the output should stay in the same order # as the input (even though it may not actually be processed in that # order). Default: true # :stop_on_exception [true|false] - if true, when an exception occurs in either # input or output, we wait for any outstanding processing to complete, # but will not process any new inputs. Default: false # :main_thread_processing [true|false] - whether the main thread pulling # on each() is allowed to process inputs. Default: true # NOTE: If you set this to false, parallelizer.kill will stop each() # in its tracks, so you need to know for sure that won't happen. def initialize(parent_task_queue, input_enumerable, options = {}, &block) @parent_task_queue = parent_task_queue @input_enumerable = input_enumerable @options = options @block = block @unconsumed_input = Queue.new @in_process = {} @unconsumed_output = Queue.new end attr_reader :parent_task_queue attr_reader :input_enumerable attr_reader :options attr_reader :block def each each_with_input do |output, index, input, type| yield output end end def each_with_index each_with_input do |output, index, input| yield output, index end end def each_with_input exception = nil each_with_exceptions do |output, index, input, type| if type == :exception if @options[:ordered] == false exception ||= output else raise output end else yield output, index, input end end raise exception if exception end def each_with_exceptions(&block) if @options[:ordered] == false each_with_exceptions_unordered(&block) else each_with_exceptions_ordered(&block) end end def wait exception = nil each_with_exceptions_unordered do |output, index, input, type| exception ||= output if type == :exception end raise exception if exception end # Enumerable methods def restricted_copy(enumerable) ParallelEnumerable.new(@parent_task_queue, enumerable, @options, &@block) end alias :original_count :count def count(*args, &block) if args.size == 0 && block.nil? @input_enumerable.count else original_count(*args, &block) end end def first(n=nil) if n restricted_copy(@input_enumerable.first(n)).to_a else first(1)[0] end end def drop(n) restricted_copy(@input_enumerable.drop(n)).to_a end def flatten(levels = nil) FlattenEnumerable.new(self, levels) end def take(n) restricted_copy(@input_enumerable.take(n)).to_a end if Enumerable.method_defined?(:lazy) class RestrictedLazy def initialize(parallel_enumerable, actual_lazy) @parallel_enumerable = parallel_enumerable @actual_lazy = actual_lazy end def drop(*args, &block) input = @parallel_enumerable.input_enumerable.lazy.drop(*args, &block) @parallel_enumerable.restricted_copy(input) end def take(*args, &block) input = @parallel_enumerable.input_enumerable.lazy.take(*args, &block) @parallel_enumerable.restricted_copy(input) end def method_missing(method, *args, &block) @actual_lazy.send(:method, *args, &block) end end alias :original_lazy :lazy def lazy RestrictedLazy.new(self, original_lazy) end end private def each_with_exceptions_unordered if @each_running raise "each() called on parallel enumerable twice simultaneously! Bad mojo" end @each_running = true begin # Grab all the inputs, yielding any responses during enumeration # in case the enumeration itself takes time begin @input_enumerable.each_with_index do |input, index| @unconsumed_input.push([ input, index ]) @parent_task_queue.push(method(:process_one)) stop_processing_input = false while !@unconsumed_output.empty? output, index, input, type = @unconsumed_output.pop yield output, index, input, type if type == :exception && @options[:stop_on_exception] stop_processing_input = true break end end if stop_processing_input break end end rescue # We still want to wait for the rest of the outputs to process @unconsumed_output.push([$!, nil, nil, :exception]) if @options[:stop_on_exception] @unconsumed_input.clear end end while !finished? # yield thread to others (for 1.8.7) if @unconsumed_output.empty? sleep(0.01) end while !@unconsumed_output.empty? yield @unconsumed_output.pop end # If no one is working on our tasks and we're allowed to # work on them in the main thread, process an input to # move things forward. if @in_process.size == 0 && !(@options[:main_thread_processing] == false) process_one end end rescue # If we exited early, perhaps due to any? finding a result, we want # to make sure and throw away any extra results (gracefully) so that # the next enumerator can start over. if !finished? stop end raise ensure @each_running = false end end def each_with_exceptions_ordered next_to_yield = 0 unconsumed = {} each_with_exceptions_unordered do |output, index, input, type| unconsumed[index] = [ output, input, type ] while unconsumed[next_to_yield] input_output = unconsumed.delete(next_to_yield) yield input_output[0], next_to_yield, input_output[1], input_output[2] next_to_yield += 1 end end input_exception = unconsumed.delete(nil) if input_exception yield input_exception[0], next_to_yield, input_exception[1], input_exception[2] end end def stop @unconsumed_input.clear while @in_process.size > 0 sleep(0.05) end @unconsumed_output.clear end # # This is thread safe only if called from the main thread pulling on each(). # The order of these checks is important, as well, to be thread safe. # 1. If @unconsumed_input.empty? is true, then we will never have any more # work legitimately picked up. # 2. If @in_process == 0, then there is no work in process, and because ofwhen unconsumed_input is empty, it will never go back up, because # this is called after the input enumerator is finished. Note that switching #2 and #1 # could cause a race, because in_process is incremented *before* consuming input. # 3. If @unconsumed_output.empty? is true, then we are done with outputs. # Thus, 1+2 means no more output will ever show up, and 3 means we've passed all # existing outputs to the user. # def finished? @unconsumed_input.empty? && @in_process.size == 0 && @unconsumed_output.empty? end def process_one @in_process[Thread.current] = true begin begin input, index = @unconsumed_input.pop(true) process_input(input, index) rescue ThreadError end ensure @in_process.delete(Thread.current) end end def process_input(input, index) begin output = @block.call(input) @unconsumed_output.push([ output, index, input, :result ]) rescue if @options[:stop_on_exception] @unconsumed_input.clear end @unconsumed_output.push([ $!, index, input, :exception ]) end index end end end end end chef-12.3.0/lib/chef/chef_fs/knife.rb0000644000004100000410000001023112520074675017230 0ustar www-datawww-data# # Author:: John Keiser () # 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 module ChefFS class Knife < Chef::Knife # Workaround for CHEF-3932 def self.deps super do require 'chef/config' require 'chef/chef_fs/parallelizer' require 'chef/chef_fs/config' require 'chef/chef_fs/file_pattern' require 'chef/chef_fs/path_utils' yield end end def self.inherited(c) super # Ensure we always get to do our includes, whether subclass calls deps or not c.deps do end c.options.merge!(options) end option :repo_mode, :long => '--repo-mode MODE', :description => "Specifies the local repository layout. Values: static, everything, hosted_everything. Default: everything/hosted_everything" option :chef_repo_path, :long => '--chef-repo-path PATH', :description => 'Overrides the location of chef repo. Default is specified by chef_repo_path in the config' option :concurrency, :long => '--concurrency THREADS', :description => 'Maximum number of simultaneous requests to send (default: 10)' def configure_chef super Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode] Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency] # --chef-repo-path forcibly overrides all other paths if config[:chef_repo_path] Chef::Config[:chef_repo_path] = config[:chef_repo_path] %w(acl client cookbook container data_bag environment group node role user).each do |variable_name| Chef::Config.delete("#{variable_name}_path".to_sym) end end @chef_fs_config = Chef::ChefFS::Config.new(Chef::Config, Dir.pwd, config, ui) Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1 end def chef_fs @chef_fs_config.chef_fs end def create_chef_fs @chef_fs_config.create_chef_fs end def local_fs @chef_fs_config.local_fs end def create_local_fs @chef_fs_config.create_local_fs end def pattern_args @pattern_args ||= pattern_args_from(name_args) end def pattern_args_from(args) args.map { |arg| pattern_arg_from(arg) } end def pattern_arg_from(arg) # TODO support absolute file paths and not just patterns? Too much? # Could be super useful in a world with multiple repo paths if !@chef_fs_config.base_path && !Chef::ChefFS::PathUtils.is_absolute?(arg) # Check if chef repo path is specified to give a better error message ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path") exit(1) end Chef::ChefFS::FilePattern.relative_to(@chef_fs_config.base_path, arg) end def format_path(entry) @chef_fs_config.format_path(entry) end def parallelize(inputs, options = {}, &block) Chef::ChefFS::Parallelizer.parallelize(inputs, options, &block) end def discover_repo_dir(dir) %w(.chef cookbooks data_bags environments roles).each do |subdir| return dir if File.directory?(File.join(dir, subdir)) end # If this isn't it, check the parent parent = File.dirname(dir) if parent && parent != dir discover_repo_dir(parent) else nil end end end end end chef-12.3.0/lib/chef/chef_fs/file_system.rb0000644000004100000410000003721112520074675020466 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/path_utils' require 'chef/chef_fs/file_system/default_environment_cannot_be_modified_error' require 'chef/chef_fs/file_system/operation_failed_error' require 'chef/chef_fs/file_system/operation_not_allowed_error' require 'chef/chef_fs/parallelizer' class Chef module ChefFS module FileSystem # Returns a list of all things under (and including) this entry that match the # given pattern. # # ==== Attributes # # * +root+ - Entry to start listing under # * +pattern+ - Chef::ChefFS::FilePattern to match children under # def self.list(root, pattern) Lister.new(root, pattern) end class Lister include Enumerable def initialize(root, pattern) @root = root @pattern = pattern end attr_reader :root attr_reader :pattern def each(&block) list_from(root, &block) end def list_from(entry, &block) # Include self in results if it matches if pattern.match?(entry.path) block.call(entry) end if pattern.could_match_children?(entry.path) # If it's possible that our children could match, descend in and add matches. exact_child_name = pattern.exact_child_name_under(entry.path) # If we've got an exact name, don't bother listing children; just grab the # child with the given name. if exact_child_name exact_child = entry.child(exact_child_name) if exact_child list_from(exact_child, &block) end # Otherwise, go through all children and find any matches elsif entry.dir? results = Parallelizer::parallelize(entry.children) { |child| Chef::ChefFS::FileSystem.list(child, pattern) } results.flatten(1).each(&block) end end end end # Resolve the given path against the entry, returning # the entry at the end of the path. # # ==== Attributes # # * +entry+ - the entry to start looking under. Relative # paths will be resolved from here. # * +path+ - the path to resolve. If it starts with +/+, # the path will be resolved starting from +entry.root+. # # ==== Examples # # Chef::ChefFS::FileSystem.resolve_path(root_path, 'cookbooks/java/recipes/default.rb') # def self.resolve_path(entry, path) return entry if path.length == 0 return resolve_path(entry.root, path) if path[0,1] == "/" && entry.root != entry if path[0,1] == "/" path = path[1,path.length-1] end result = entry Chef::ChefFS::PathUtils::split(path).each do |part| result = result.child(part) end result end # Copy everything matching the given pattern from src to dest. # # After this method completes, everything in dest matching the # given pattern will look identical to src. # # ==== Attributes # # * +pattern+ - Chef::ChefFS::FilePattern to match children under # * +src_root+ - the root from which things will be copied # * +dest_root+ - the root to which things will be copied # * +recurse_depth+ - the maximum depth to copy things. +nil+ # means infinite depth. 0 means no recursion. # * +options+ - hash of options: # - +purge+ - if +true+, items in +dest+ that are not in +src+ # will be deleted from +dest+. If +false+, these items will # be left alone. # - +force+ - if +true+, matching files are always copied from # +src+ to +dest+. If +false+, they will only be copied if # actually different (which will take time to determine). # - +dry_run+ - if +true+, action will not actually be taken; # things will be printed out instead. # # ==== Examples # # Chef::ChefFS::FileSystem.copy_to(FilePattern.new('/cookbooks'), # chef_fs, local_fs, nil, true) do |message| # puts message # end # def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui = nil, format_path = nil) found_result = false error = false parallel_do(list_pairs(pattern, src_root, dest_root)) do |src, dest| found_result = true new_dest_parent = get_or_create_parent(dest, options, ui, format_path) child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path) error ||= child_error end if !found_result && pattern.exact_path ui.error "#{pattern}: No such file or directory on remote or local" if ui error = true end error end # Yield entries for children that are in either +a_root+ or +b_root+, with # matching pairs matched up. # # ==== Yields # # Yields matching entries in pairs: # # [ a_entry, b_entry ] # # ==== Example # # Chef::ChefFS::FileSystem.list_pairs(FilePattern.new('**x.txt', a_root, b_root)).each do |a, b| # ... # end # def self.list_pairs(pattern, a_root, b_root) PairLister.new(pattern, a_root, b_root) end class PairLister include Enumerable def initialize(pattern, a_root, b_root) @pattern = pattern @a_root = a_root @b_root = b_root end attr_reader :pattern attr_reader :a_root attr_reader :b_root def each # Make sure everything on the server is also on the filesystem, and diff found_paths = Set.new Chef::ChefFS::FileSystem.list(a_root, pattern).each do |a| found_paths << a.path b = Chef::ChefFS::FileSystem.resolve_path(b_root, a.path) yield [ a, b ] end # Check the outer regex pattern to see if it matches anything on the # filesystem that isn't on the server Chef::ChefFS::FileSystem.list(b_root, pattern).each do |b| if !found_paths.include?(b.path) a = Chef::ChefFS::FileSystem.resolve_path(a_root, b.path) yield [ a, b ] end end end end # Get entries for children of either a or b, with matching pairs matched up. # # ==== Returns # # An array of child pairs. # # [ [ a_child, b_child ], ... ] # # If a child is only in a or only in b, the other child entry will be # retrieved by name (and will most likely be a "nonexistent child"). # # ==== Example # # Chef::ChefFS::FileSystem.child_pairs(a, b).length # def self.child_pairs(a, b) # If both are directories, recurse into them and diff the children instead of returning ourselves. result = [] a_children_names = Set.new a.children.each do |a_child| a_children_names << a_child.name result << [ a_child, b.child(a_child.name) ] end # Check b for children that aren't in a b.children.each do |b_child| if !a_children_names.include?(b_child.name) result << [ a.child(b_child.name), b_child ] end end result end def self.compare(a, b) are_same, a_value, b_value = a.compare_to(b) if are_same.nil? are_same, b_value, a_value = b.compare_to(a) end if are_same.nil? # TODO these reads can be parallelized begin a_value = a.read if a_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError a_value = :none end begin b_value = b.read if b_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError b_value = :none end are_same = (a_value == b_value) end [ are_same, a_value, b_value ] end private # Copy two entries (could be files or dirs) def self.copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path) # A NOTE about this algorithm: # There are cases where this algorithm does too many network requests. # knife upload with a specific filename will first check if the file # exists (a "dir" in the parent) before deciding whether to POST or # PUT it. If we just tried PUT (or POST) and then tried the other if # the conflict failed, we wouldn't need to check existence. # On the other hand, we may already have DONE the request, in which # case we shouldn't waste time trying PUT if we know the file doesn't # exist. # Will need to decide how that works with checksums, though. error = false begin dest_path = format_path.call(dest_entry) if ui src_path = format_path.call(src_entry) if ui if !src_entry.exists? if options[:purge] # If we would not have uploaded it, we will not purge it. if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?) if options[:dry_run] ui.output "Would delete #{dest_path}" if ui else begin dest_entry.delete(true) ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui rescue Chef::ChefFS::FileSystem::NotFoundError ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui end end else ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui end end elsif !dest_entry.exists? if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?) # If the entry can do a copy directly from filesystem, do that. if new_dest_parent.respond_to?(:create_child_from) if options[:dry_run] ui.output "Would create #{dest_path}" if ui else new_dest_parent.create_child_from(src_entry) ui.output "Created #{dest_path}" if ui end return end if src_entry.dir? if options[:dry_run] ui.output "Would create #{dest_path}" if ui new_dest_dir = new_dest_parent.child(src_entry.name) else new_dest_dir = new_dest_parent.create_child(src_entry.name, nil) ui.output "Created #{dest_path}" if ui end # Directory creation is recursive. if recurse_depth != 0 parallel_do(src_entry.children) do |src_child| new_dest_child = new_dest_dir.child(src_child.name) child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) error ||= child_error end end else if options[:dry_run] ui.output "Would create #{dest_path}" if ui else new_dest_parent.create_child(src_entry.name, src_entry.read) ui.output "Created #{dest_path}" if ui end end end else # Both exist. # If the entry can do a copy directly, do that. if dest_entry.respond_to?(:copy_from) if options[:force] || compare(src_entry, dest_entry)[0] == false if options[:dry_run] ui.output "Would update #{dest_path}" if ui else dest_entry.copy_from(src_entry, options) ui.output "Updated #{dest_path}" if ui end end return end # If they are different types, log an error. if src_entry.dir? if dest_entry.dir? # If both are directories, recurse into their children if recurse_depth != 0 parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child| child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) error ||= child_error end end else # If they are different types. ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui return end else if dest_entry.dir? ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui return else # Both are files! Copy them unless we're sure they are the same.' if options[:diff] == false should_copy = false elsif options[:force] should_copy = true src_value = nil else are_same, src_value, _dest_value = compare(src_entry, dest_entry) should_copy = !are_same end if should_copy if options[:dry_run] ui.output "Would update #{dest_path}" if ui else src_value = src_entry.read if src_value.nil? dest_entry.write(src_value) ui.output "Updated #{dest_path}" if ui end end end end end rescue DefaultEnvironmentCannotBeModifiedError => e ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui rescue OperationFailedError => e ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui error = true rescue OperationNotAllowedError => e ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui error = true end error end def self.get_or_create_parent(entry, options, ui, format_path) parent = entry.parent if parent && !parent.exists? parent_path = format_path.call(parent) if ui parent_parent = get_or_create_parent(parent, options, ui, format_path) if options[:dry_run] ui.output "Would create #{parent_path}" if ui else parent = parent_parent.create_child(parent.name, nil) ui.output "Created #{parent_path}" if ui end end return parent end def self.parallel_do(enum, options = {}, &block) Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block) end end end end chef-12.3.0/lib/chef/chef_fs/command_line.rb0000644000004100000410000002523112520074675020567 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system' require 'chef/chef_fs/file_system/operation_failed_error' require 'chef/chef_fs/file_system/operation_not_allowed_error' require 'chef/util/diff' class Chef module ChefFS module CommandLine def self.diff_print(pattern, a_root, b_root, recurse_depth, output_mode, format_path = nil, diff_filter = nil, ui = nil) if format_path.nil? format_path = proc { |entry| entry.path_for_printing } end get_content = (output_mode != :name_only && output_mode != :name_status) found_match = false diff(pattern, a_root, b_root, recurse_depth, get_content).each do |type, old_entry, new_entry, old_value, new_value, error| found_match = true unless type == :both_nonexistent old_path = format_path.call(old_entry) new_path = format_path.call(new_entry) case type when :common_subdirectories if output_mode != :name_only && output_mode != :name_status yield "Common subdirectories: #{new_path}\n" end when :directory_to_file next if diff_filter && diff_filter !~ /T/ if output_mode == :name_only yield "#{new_path}\n" elsif output_mode == :name_status yield "T\t#{new_path}\n" else yield "File #{old_path} is a directory while file #{new_path} is a regular file\n" end when :file_to_directory next if diff_filter && diff_filter !~ /T/ if output_mode == :name_only yield "#{new_path}\n" elsif output_mode == :name_status yield "T\t#{new_path}\n" else yield "File #{old_path} is a regular file while file #{new_path} is a directory\n" end when :deleted next if diff_filter && diff_filter !~ /D/ if output_mode == :name_only yield "#{new_path}\n" elsif output_mode == :name_status yield "D\t#{new_path}\n" elsif old_value result = "diff --knife #{old_path} #{new_path}\n" result << "deleted file\n" result << diff_text(old_path, '/dev/null', old_value, '') yield result else yield "Only in #{format_path.call(old_entry.parent)}: #{old_entry.name}\n" end when :added next if diff_filter && diff_filter !~ /A/ if output_mode == :name_only yield "#{new_path}\n" elsif output_mode == :name_status yield "A\t#{new_path}\n" elsif new_value result = "diff --knife #{old_path} #{new_path}\n" result << "new file\n" result << diff_text('/dev/null', new_path, '', new_value) yield result else yield "Only in #{format_path.call(new_entry.parent)}: #{new_entry.name}\n" end when :modified next if diff_filter && diff_filter !~ /M/ if output_mode == :name_only yield "#{new_path}\n" elsif output_mode == :name_status yield "M\t#{new_path}\n" else result = "diff --knife #{old_path} #{new_path}\n" result << diff_text(old_path, new_path, old_value, new_value) yield result end when :both_nonexistent when :added_cannot_upload when :deleted_cannot_download when :same # Skip these silently when :error if error.is_a?(Chef::ChefFS::FileSystem::OperationFailedError) ui.error "#{format_path.call(error.entry)} failed to #{error.operation}: #{error.message}" if ui error = true elsif error.is_a?(Chef::ChefFS::FileSystem::OperationNotAllowedError) ui.error "#{format_path.call(error.entry)} #{error.reason}." if ui else raise error end end end if !found_match ui.error "#{pattern}: No such file or directory on remote or local" if ui error = true end error end def self.diff(pattern, old_root, new_root, recurse_depth, get_content) Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.list_pairs(pattern, old_root, new_root)) do |old_entry, new_entry| diff_entries(old_entry, new_entry, recurse_depth, get_content) end.flatten(1) end # Diff two known entries (could be files or dirs) def self.diff_entries(old_entry, new_entry, recurse_depth, get_content) # If both are directories if old_entry.dir? if new_entry.dir? if recurse_depth == 0 return [ [ :common_subdirectories, old_entry, new_entry ] ] else return Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry)) do |old_child, new_child| Chef::ChefFS::CommandLine.diff_entries(old_child, new_child, recurse_depth ? recurse_depth - 1 : nil, get_content) end.flatten(1) end # If old is a directory and new is a file elsif new_entry.exists? return [ [ :directory_to_file, old_entry, new_entry ] ] # If old is a directory and new does not exist elsif new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?) return [ [ :deleted, old_entry, new_entry ] ] # If the new entry does not and *cannot* exist, report that. else return [ [ :new_cannot_upload, old_entry, new_entry ] ] end # If new is a directory and old is a file elsif new_entry.dir? if old_entry.exists? return [ [ :file_to_directory, old_entry, new_entry ] ] # If new is a directory and old does not exist elsif old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?) return [ [ :added, old_entry, new_entry ] ] # If the new entry does not and *cannot* exist, report that. else return [ [ :old_cannot_upload, old_entry, new_entry ] ] end # Neither is a directory, so they are diffable with file diff else are_same, old_value, new_value = Chef::ChefFS::FileSystem.compare(old_entry, new_entry) if are_same if old_value == :none return [ [ :both_nonexistent, old_entry, new_entry ] ] else return [ [ :same, old_entry, new_entry ] ] end else if old_value == :none old_exists = false elsif old_value.nil? old_exists = old_entry.exists? else old_exists = true end if new_value == :none new_exists = false elsif new_value.nil? new_exists = new_entry.exists? else new_exists = true end # If one of the files doesn't exist, we only want to print the diff if the # other file *could be uploaded/downloaded*. if !old_exists && !old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?) return [ [ :old_cannot_upload, old_entry, new_entry ] ] end if !new_exists && !new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?) return [ [ :new_cannot_upload, old_entry, new_entry ] ] end if get_content # If we haven't read the values yet, get them now so that they can be diffed begin old_value = old_entry.read if old_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError old_value = :none end begin new_value = new_entry.read if new_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError new_value = :none end end if old_value == :none || (old_value == nil && !old_entry.exists?) return [ [ :added, old_entry, new_entry, old_value, new_value ] ] elsif new_value == :none return [ [ :deleted, old_entry, new_entry, old_value, new_value ] ] else return [ [ :modified, old_entry, new_entry, old_value, new_value ] ] end end end rescue Chef::ChefFS::FileSystem::FileSystemError => e return [ [ :error, old_entry, new_entry, nil, nil, e ] ] end private def self.sort_keys(json_object) if json_object.is_a?(Array) json_object.map { |o| sort_keys(o) } elsif json_object.is_a?(Hash) new_hash = {} json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) } new_hash else json_object end end def self.canonicalize_json(json_text) parsed_json = Chef::JSONCompat.parse(json_text) sorted_json = sort_keys(parsed_json) Chef::JSONCompat.to_json_pretty(sorted_json) end def self.diff_text(old_path, new_path, old_value, new_value) # Copy to tempfiles before diffing # TODO don't copy things that are already in files! Or find an in-memory diff algorithm begin new_tempfile = Tempfile.new("new") new_tempfile.write(new_value) new_tempfile.close begin old_tempfile = Tempfile.new("old") old_tempfile.write(old_value) old_tempfile.close result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path) result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}") result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}") result ensure old_tempfile.close! end ensure new_tempfile.close! end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/0000755000004100000410000000000012520074675020135 5ustar www-datawww-datachef-12.3.0/lib/chef/chef_fs/file_system/cookbook_file.rb0000644000004100000410000000440412520074675023271 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_object' require 'chef/http/simple' require 'openssl' class Chef module ChefFS module FileSystem class CookbookFile < BaseFSObject def initialize(name, parent, file) super(name, parent) @file = file end attr_reader :file def checksum file[:checksum] end def read begin tmpfile = rest.streaming_request(file[:url]) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "Timeout reading #{file[:url]}: #{e}" rescue Net::HTTPServerException => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "#{e.message} retrieving #{file[:url]}" end begin tmpfile.open tmpfile.read ensure tmpfile.close! end end def rest parent.rest end def compare_to(other) other_value = nil if other.respond_to?(:checksum) other_checksum = other.checksum else begin other_value = other.read rescue Chef::ChefFS::FileSystem::NotFoundError return [ false, nil, :none ] end other_checksum = calc_checksum(other_value) end [ checksum == other_checksum, nil, other_value ] end private def calc_checksum(value) OpenSSL::Digest::MD5.hexdigest(value) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/acl_entry.rb0000644000004100000410000000407612520074675022451 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/operation_not_allowed_error' require 'chef/chef_fs/file_system/operation_failed_error' class Chef module ChefFS module FileSystem class AclEntry < RestListEntry PERMISSIONS = %w(create read update delete grant) def api_path "#{super}/_acl" end def delete(recurse) raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self), "ACLs cannot be deleted." end def write(file_contents) # ACL writes are fun. acls = data_handler.normalize(Chef::JSONCompat.parse(file_contents), self) PERMISSIONS.each do |permission| begin rest.put("#{api_path}/#{permission}", { permission => acls[permission] }) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Timeout writing: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "HTTP error writing: #{e}" end end end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/nodes_dir.rb0000644000004100000410000000374212520074675022436 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/data_handler/node_data_handler' class Chef module ChefFS module FileSystem class NodesDir < RestListDir def initialize(parent) super("nodes", parent, nil, Chef::ChefFS::DataHandler::NodeDataHandler.new) end # Identical to RestListDir.children, except supports environments def children begin @children ||= root.get_json(env_api_path).keys.sort.map do |key| _make_child_entry("#{key}.json", true) end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "Timeout retrieving children: #{e}" rescue Net::HTTPServerException => e if $!.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "HTTP error retrieving children: #{e}" end end end def env_api_path environment ? "environments/#{environment}/#{api_path}" : api_path end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_acls_dir.rb0000644000004100000410000000244312520074675030134 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/file_system/acls_dir' require 'chef/chef_fs/data_handler/acl_data_handler' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemAclsDir < ChefRepositoryFileSystemEntry def initialize(name, parent, path = nil) super(name, parent, path, Chef::ChefFS::DataHandler::AclDataHandler.new) end def can_have_child?(name, is_dir) is_dir ? Chef::ChefFS::FileSystem::AclsDir::ENTITY_TYPES.include?(name) : name == 'organization.json' end end end end endchef-12.3.0/lib/chef/chef_fs/file_system/organization_invites_entry.rb0000644000004100000410000000373412520074675026157 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/data_handler/organization_invites_data_handler' require 'chef/json_compat' class Chef module ChefFS module FileSystem # /organizations/NAME/invitations.json # read data from: # - GET /organizations/NAME/association_requests # write data to: # - remove from list: DELETE /organizations/NAME/association_requests/id # - add to list: POST /organizations/NAME/association_requests class OrganizationInvitesEntry < RestListEntry def initialize(name, parent, exists = nil) super(name, parent) @exists = exists end def data_handler Chef::ChefFS::DataHandler::OrganizationInvitesDataHandler.new end # /organizations/foo/invites.json -> /organizations/foo/association_requests def api_path File.join(parent.api_path, 'association_requests') end def exists? parent.exists? end def delete(recurse) raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self) end def write(contents) desired_invites = minimize_value(Chef::JSONCompat.parse(contents, :create_additions => false)) actual_invites = _read_json.inject({}) { |h,val| h[val['username']] = val['id']; h } invites = actual_invites.keys (desired_invites - invites).each do |invite| begin rest.post(api_path, { 'user' => invite }) rescue Net::HTTPServerException => e if e.response.code == '409' Chef::Log.warn("Could not invite #{invite} to organization #{org}: #{api_error_text(e.response)}") else raise end end end (invites - desired_invites).each do |invite| rest.delete(File.join(api_path, actual_invites[invite])) end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_server_root_dir.rb0000644000004100000410000001413212520074675024657 0ustar www-datawww-data# # Author:: John Keiser () # 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/server_api' require 'chef/chef_fs/file_system/acls_dir' require 'chef/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/cookbooks_dir' require 'chef/chef_fs/file_system/data_bags_dir' require 'chef/chef_fs/file_system/nodes_dir' require 'chef/chef_fs/file_system/org_entry' require 'chef/chef_fs/file_system/organization_invites_entry' require 'chef/chef_fs/file_system/organization_members_entry' require 'chef/chef_fs/file_system/environments_dir' require 'chef/chef_fs/data_handler/client_data_handler' require 'chef/chef_fs/data_handler/role_data_handler' require 'chef/chef_fs/data_handler/user_data_handler' require 'chef/chef_fs/data_handler/group_data_handler' require 'chef/chef_fs/data_handler/container_data_handler' class Chef module ChefFS module FileSystem # # Represents the root of a Chef server (or organization), under which # nodes, roles, cookbooks, etc. can be found. # class ChefServerRootDir < BaseFSDir # # Create a new Chef server root. # # == Parameters # # [root_name] # A friendly name for the root, for printing--like "remote" or "chef_central". # [chef_config] # A hash with options that look suspiciously like Chef::Config, including the # following keys: # :chef_server_url:: The URL to the Chef server or top of the organization # :node_name:: The username to authenticate to the Chef server with # :client_key:: The private key for the user for authentication # :environment:: The environment in which you are presently working # :repo_mode:: # The repository mode, :hosted_everything, :everything or :static. # This determines the set of subdirectories the Chef server will # offer up. # :versioned_cookbooks:: whether or not to include versions in cookbook names # [options] # Other options: # :cookbook_version:: when cookbooks are retrieved, grab this version for them. # :freeze:: freeze cookbooks on upload # def initialize(root_name, chef_config, options = {}) super("", nil) @chef_server_url = chef_config[:chef_server_url] @chef_username = chef_config[:node_name] @chef_private_key = chef_config[:client_key] @environment = chef_config[:environment] @repo_mode = chef_config[:repo_mode] @versioned_cookbooks = chef_config[:versioned_cookbooks] @root_name = root_name @cookbook_version = options[:cookbook_version] # Used in knife diff and download for server cookbook version end attr_reader :chef_server_url attr_reader :chef_username attr_reader :chef_private_key attr_reader :environment attr_reader :repo_mode attr_reader :cookbook_version attr_reader :versioned_cookbooks def fs_description "Chef server at #{chef_server_url} (user #{chef_username}), repo_mode = #{repo_mode}" end def rest Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true) end def get_json(path) Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key).get(path) end def chef_rest Chef::REST.new(chef_server_url, chef_username, chef_private_key) end def api_path "" end def path_for_printing "#{@root_name}/" end def can_have_child?(name, is_dir) is_dir && children.any? { |child| child.name == name } end def org @org ||= begin path = Pathname.new(URI.parse(chef_server_url).path).cleanpath if File.dirname(path) == '/organizations' File.basename(path) else nil end end end def children @children ||= begin result = [ CookbooksDir.new(self), DataBagsDir.new(self), EnvironmentsDir.new(self), RestListDir.new("roles", self, nil, Chef::ChefFS::DataHandler::RoleDataHandler.new) ] if repo_mode == 'hosted_everything' result += [ AclsDir.new(self), RestListDir.new("clients", self, nil, Chef::ChefFS::DataHandler::ClientDataHandler.new), RestListDir.new("containers", self, nil, Chef::ChefFS::DataHandler::ContainerDataHandler.new), RestListDir.new("groups", self, nil, Chef::ChefFS::DataHandler::GroupDataHandler.new), NodesDir.new(self), OrgEntry.new("org.json", self), OrganizationMembersEntry.new("members.json", self), OrganizationInvitesEntry.new("invitations.json", self) ] elsif repo_mode != 'static' result += [ RestListDir.new("clients", self, nil, Chef::ChefFS::DataHandler::ClientDataHandler.new), NodesDir.new(self), RestListDir.new("users", self, nil, Chef::ChefFS::DataHandler::UserDataHandler.new) ] end result.sort_by { |child| child.name } end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/data_bag_dir.rb0000644000004100000410000000447212520074675023051 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/must_delete_recursively_error' require 'chef/chef_fs/data_handler/data_bag_item_data_handler' class Chef module ChefFS module FileSystem class DataBagDir < RestListDir def initialize(name, parent, exists = nil) super(name, parent, nil, Chef::ChefFS::DataHandler::DataBagItemDataHandler.new) @exists = nil end def dir? exists? end def read # This will only be called if dir? is false, which means exists? is false. raise Chef::ChefFS::FileSystem::NotFoundError.new(self) end def exists? if @exists.nil? @exists = parent.children.any? { |child| child.name == name } end @exists end def delete(recurse) if !recurse raise NotFoundError.new(self) if !exists? raise MustDeleteRecursivelyError.new(self), "#{path_for_printing} must be deleted recursively" end begin rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "HTTP error deleting: #{e}" end end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/memory_file.rb0000644000004100000410000000054412520074675022774 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/base_fs_object' class Chef module ChefFS module FileSystem class MemoryFile < Chef::ChefFS::FileSystem::BaseFSObject def initialize(name, parent, value) super(name, parent) @value = value end def read return @value end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/environments_dir.rb0000644000004100000410000000404212520074675024047 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/default_environment_cannot_be_modified_error' require 'chef/chef_fs/data_handler/environment_data_handler' class Chef module ChefFS module FileSystem class EnvironmentsDir < RestListDir def initialize(parent) super("environments", parent, nil, Chef::ChefFS::DataHandler::EnvironmentDataHandler.new) end def _make_child_entry(name, exists = nil) if name == '_default.json' DefaultEnvironmentEntry.new(name, self, exists) else super end end class DefaultEnvironmentEntry < RestListEntry def initialize(name, parent, exists = nil) super(name, parent) @exists = exists end def delete(recurse) raise NotFoundError.new(self) if !exists? raise DefaultEnvironmentCannotBeModifiedError.new(:delete, self), "#{path_for_printing} cannot be deleted." end def write(file_contents) raise NotFoundError.new(self) if !exists? raise DefaultEnvironmentCannotBeModifiedError.new(:write, self), "#{path_for_printing} cannot be updated." end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/file_system_error.rb0000644000004100000410000000170612520074675024222 0ustar www-datawww-data# # Author:: John Keiser () # 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. # class Chef module ChefFS module FileSystem class FileSystemError < StandardError def initialize(entry, cause = nil) @entry = entry @cause = cause end attr_reader :entry attr_reader :cause end end end end chef-12.3.0/lib/chef/chef_fs/file_system/data_bags_dir.rb0000644000004100000410000000521612520074675023231 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/data_bag_dir' class Chef module ChefFS module FileSystem class DataBagsDir < RestListDir def initialize(parent) super("data_bags", parent, "data") end def child(name) result = @children.select { |child| child.name == name }.first if @children result || DataBagDir.new(name, self) end def children begin @children ||= root.get_json(api_path).keys.sort.map do |entry| DataBagDir.new(entry, self, true) end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "Timeout getting children: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "HTTP error getting children: #{e}" end end end def can_have_child?(name, is_dir) is_dir end def create_child(name, file_contents) begin rest.post(api_path, { 'name' => name }) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Timeout creating child '#{name}': #{e}" rescue Net::HTTPServerException => e if e.response.code == "409" raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self, e), "Cannot create #{name} under #{path}: already exists" else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "HTTP error creating child '#{name}': #{e}" end end @children = nil DataBagDir.new(name, self, true) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/memory_root.rb0000644000004100000410000000070512520074675023037 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/memory_dir' class Chef module ChefFS module FileSystem class MemoryRoot < MemoryDir def initialize(pretty_name, cannot_be_in_regex = nil) super('', nil) @pretty_name = pretty_name @cannot_be_in_regex = cannot_be_in_regex end attr_reader :cannot_be_in_regex def path_for_printing @pretty_name end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/organization_members_entry.rb0000644000004100000410000000361512520074675026126 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/data_handler/organization_members_data_handler' require 'chef/json_compat' class Chef module ChefFS module FileSystem # /organizations/NAME/members.json # reads data from: # - GET /organizations/NAME/users # writes data to: # - remove from list: DELETE /organizations/NAME/users/name # - add to list: POST /organizations/NAME/users/name class OrganizationMembersEntry < RestListEntry def initialize(name, parent, exists = nil) super(name, parent) @exists = exists end def data_handler Chef::ChefFS::DataHandler::OrganizationMembersDataHandler.new end # /organizations/foo/members.json -> /organizations/foo/users def api_path File.join(parent.api_path, 'users') end def exists? parent.exists? end def delete(recurse) raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self) end def write(contents) desired_members = minimize_value(Chef::JSONCompat.parse(contents, :create_additions => false)) members = minimize_value(_read_json) (desired_members - members).each do |member| begin rest.post(File.join(api_path, member), {}) rescue Net::HTTPServerException => e if e.response.code == '404' raise "Chef server at #{api_path} does not allow you to directly add members. Please either upgrade your Chef server or move the users you want into invitations.json instead of members.json." else raise end end end (members - desired_members).each do |member| rest.delete(File.join(api_path, member)) end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/nonexistent_fs_object.rb0000644000004100000410000000200112520074675025047 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_object' require 'chef/chef_fs/file_system/not_found_error' class Chef module ChefFS module FileSystem class NonexistentFSObject < BaseFSObject def initialize(name, parent) super end def exists? false end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/cookbook_dir.rb0000644000004100000410000002073212520074675023132 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/cookbook_subdir' require 'chef/chef_fs/file_system/cookbook_file' require 'chef/chef_fs/file_system/not_found_error' require 'chef/cookbook_version' require 'chef/cookbook_uploader' class Chef module ChefFS module FileSystem class CookbookDir < BaseFSDir def initialize(name, parent, options = {}) super(name, parent) @exists = options[:exists] # If the name is apache2-1.0.0 and versioned_cookbooks is on, we know # the actual cookbook_name and version. if root.versioned_cookbooks if name =~ VALID_VERSIONED_COOKBOOK_NAME @cookbook_name = $1 @version = $2 else @exists = false end else @cookbook_name = name @version = root.cookbook_version # nil unless --cookbook-version specified in download/diff end end attr_reader :cookbook_name, :version COOKBOOK_SEGMENT_INFO = { :attributes => { :ruby_only => true }, :definitions => { :ruby_only => true }, :recipes => { :ruby_only => true }, :libraries => { :ruby_only => true }, :templates => { :recursive => true }, :files => { :recursive => true }, :resources => { :ruby_only => true, :recursive => true }, :providers => { :ruby_only => true, :recursive => true }, :root_files => { } } # See Erchef code # https://github.com/opscode/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94 VALID_VERSIONED_COOKBOOK_NAME = /^([.a-zA-Z0-9_-]+)-(\d+\.\d+\.\d+)$/ def add_child(child) @children << child end def api_path "#{parent.api_path}/#{cookbook_name}/#{version || "_latest"}" end def child(name) # Since we're ignoring the rules and doing a network request here, # we need to make sure we don't rethrow the exception. (child(name) # is not supposed to fail.) begin result = children.select { |child| child.name == name }.first return result if result rescue Chef::ChefFS::FileSystem::NotFoundError end return NonexistentFSObject.new(name, self) end def can_have_child?(name, is_dir) # A cookbook's root may not have directories unless they are segment directories return name != 'root_files' && COOKBOOK_SEGMENT_INFO.keys.include?(name.to_sym) if is_dir return true end def children if @children.nil? @children = [] manifest = chef_object.manifest COOKBOOK_SEGMENT_INFO.each do |segment, segment_info| next unless manifest.has_key?(segment) # Go through each file in the manifest for the segment, and # add cookbook subdirs and files for it. manifest[segment].each do |segment_file| parts = segment_file[:path].split('/') # Get or create the path to the file container = self parts[0,parts.length-1].each do |part| old_container = container container = old_container.children.select { |child| part == child.name }.first if !container container = CookbookSubdir.new(part, old_container, segment_info[:ruby_only], segment_info[:recursive]) old_container.add_child(container) end end # Create the file itself container.add_child(CookbookFile.new(parts[parts.length-1], container, segment_file)) end end @children = @children.sort_by { |c| c.name } end @children end def dir? exists? end def delete(recurse) if recurse begin rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException if $!.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "HTTP error deleting: #{e}" end end else raise NotFoundError.new(self) if !exists? raise MustDeleteRecursivelyError.new(self), "#{path_for_printing} must be deleted recursively" end end # In versioned cookbook mode, actually check if the version exists # Probably want to cache this. def exists? if @exists.nil? @exists = parent.children.any? { |child| child.name == name } end @exists end def compare_to(other) if !other.dir? return [ !exists?, nil, nil ] end are_same = true Chef::ChefFS::CommandLine::diff_entries(self, other, nil, :name_only).each do |type, old_entry, new_entry| if [ :directory_to_file, :file_to_directory, :deleted, :added, :modified ].include?(type) are_same = false end end [ are_same, nil, nil ] end def copy_from(other, options = {}) parent.upload_cookbook_from(other, options) end def rest parent.rest end def chef_object # We cheat and cache here, because it seems like a good idea to keep # the cookbook view consistent with the directory structure. return @chef_object if @chef_object # The negative (not found) response is cached if @could_not_get_chef_object raise Chef::ChefFS::FileSystem::NotFoundError.new(self, @could_not_get_chef_object) end begin # We want to fail fast, for now, because of the 500 issue :/ # This will make things worse for parallelism, a little, because # Chef::Config is global and this could affect other requests while # this request is going on. (We're not parallel yet, but we will be.) # Chef bug http://tickets.opscode.com/browse/CHEF-3066 old_retry_count = Chef::Config[:http_retry_count] begin Chef::Config[:http_retry_count] = 0 @chef_object ||= Chef::CookbookVersion.json_create(root.get_json(api_path)) ensure Chef::Config[:http_retry_count] = old_retry_count end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "Timeout reading: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" @could_not_get_chef_object = e raise Chef::ChefFS::FileSystem::NotFoundError.new(self, @could_not_get_chef_object) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "HTTP error reading: #{e}" end # Chef bug http://tickets.opscode.com/browse/CHEF-3066 ... instead of 404 we get 500 right now. # Remove this when that bug is fixed. rescue Net::HTTPFatalError => e if e.response.code == "500" @could_not_get_chef_object = e raise Chef::ChefFS::FileSystem::NotFoundError.new(self, @could_not_get_chef_object) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "HTTP error reading: #{e}" end end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/operation_failed_error.rb0000644000004100000410000000234012520074675025176 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/file_system_error' class Chef module ChefFS module FileSystem class OperationFailedError < FileSystemError def initialize(operation, entry, cause = nil) super(entry, cause) @operation = operation end def message if cause && cause.is_a?(Net::HTTPExceptions) && cause.response.code == "400" "#{super} cause: #{cause.response.body}" else super end end attr_reader :operation end end end end chef-12.3.0/lib/chef/chef_fs/file_system/acl_dir.rb0000644000004100000410000000406112520074675022060 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/acl_entry' require 'chef/chef_fs/file_system/operation_not_allowed_error' class Chef module ChefFS module FileSystem class AclDir < BaseFSDir def api_path parent.parent.child(name).api_path end def child(name) result = @children.select { |child| child.name == name }.first if @children result ||= can_have_child?(name, false) ? AclEntry.new(name, self) : NonexistentFSObject.new(name, self) end def can_have_child?(name, is_dir) name =~ /\.json$/ && !is_dir end def children if @children.nil? # Grab the ACTUAL children (/nodes, /containers, etc.) and get their names names = parent.parent.child(name).children.map { |child| child.dir? ? "#{child.name}.json" : child.name } @children = names.map { |name| AclEntry.new(name, self, true) } end @children end def create_child(name, file_contents) raise OperationNotAllowedError.new(:create_child, self), "ACLs can only be updated, and can only be created when the corresponding object is created." end def data_handler parent.data_handler end def rest parent.rest end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb0000644000004100000410000000215412520074675031615 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/operation_not_allowed_error' class Chef module ChefFS module FileSystem class DefaultEnvironmentCannotBeModifiedError < OperationNotAllowedError def initialize(operation, entry, cause = nil) super(operation, entry, cause) end def reason result = super result + " (default environment cannot be modified)" end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir.rb0000644000004100000410000000646012520074675031206 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir' require 'chef/cookbook/chefignore' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemCookbooksDir < ChefRepositoryFileSystemEntry def initialize(name, parent, file_path) super(name, parent, file_path) begin @chefignore = Chef::Cookbook::Chefignore.new(self.file_path) rescue Errno::EISDIR rescue Errno::EACCES # Work around a bug in Chefignore when chefignore is a directory end end attr_reader :chefignore def children begin Dir.entries(file_path).sort. select { |child_name| can_have_child?(child_name, File.directory?(File.join(file_path, child_name))) }. map { |child_name| make_child(child_name) }. select do |entry| # empty cookbooks and cookbook directories are ignored if !entry.can_upload? Chef::Log.warn("Cookbook '#{entry.name}' is empty or entirely chefignored at #{entry.path_for_printing}") false else true end end rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end def can_have_child?(name, is_dir) is_dir && !name.start_with?('.') end def write_cookbook(cookbook_path, cookbook_version_json, from_fs) cookbook_name = File.basename(cookbook_path) child = make_child(cookbook_name) # Use the copy/diff algorithm to copy it down so we don't destroy # chefignored data. This is terribly un-thread-safe. Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new("/#{cookbook_path}"), from_fs, child, nil, {:purge => true}) # Write out .uploaded-cookbook-version.json cookbook_file_path = File.join(file_path, cookbook_name) if !File.exists?(cookbook_file_path) FileUtils.mkdir_p(cookbook_file_path) end uploaded_cookbook_version_path = File.join(cookbook_file_path, Chef::Cookbook::CookbookVersionLoader::UPLOADED_COOKBOOK_VERSION_FILE) File.open(uploaded_cookbook_version_path, 'w') do |file| file.write(cookbook_version_json) end end protected def make_child(child_name) ChefRepositoryFileSystemCookbookDir.new(child_name, self) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/org_entry.rb0000644000004100000410000000155512520074675022500 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/data_handler/organization_data_handler' class Chef module ChefFS module FileSystem # /organizations/NAME/org.json # Represents the actual data at /organizations/NAME (the full name, etc.) class OrgEntry < RestListEntry def initialize(name, parent, exists = nil) super(name, parent) @exists = exists end def data_handler Chef::ChefFS::DataHandler::OrganizationDataHandler.new end # /organizations/foo/org.json -> GET /organizations/foo def api_path parent.api_path end def exists? parent.exists? end def delete(recurse) raise Chef::ChefFS::FileSystem::OperationNotAllowedError.new(:delete, self) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/base_fs_dir.rb0000644000004100000410000000265612520074675022733 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_object' require 'chef/chef_fs/file_system/nonexistent_fs_object' class Chef module ChefFS module FileSystem class BaseFSDir < BaseFSObject def initialize(name, parent) super end def dir? true end # Override child(name) to provide a child object by name without the network read def child(name) children.select { |child| child.name == name }.first || NonexistentFSObject.new(name, self) end def can_have_child?(name, is_dir) true end # An empty children array is an empty dir def empty? children.empty? end # Abstract: children end end end end chef-12.3.0/lib/chef/chef_fs/file_system/cookbook_subdir.rb0000644000004100000410000000264212520074675023644 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' class Chef module ChefFS module FileSystem class CookbookSubdir < BaseFSDir def initialize(name, parent, ruby_only, recursive) super(name, parent) @children = [] @ruby_only = ruby_only @recursive = recursive end attr_reader :versions attr_reader :children def add_child(child) @children << child end def can_have_child?(name, is_dir) if is_dir return false if !@recursive else return false if @ruby_only && name !~ /\.rb$/ end true end def rest parent.rest end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/rest_list_dir.rb0000644000004100000410000001004612520074675023331 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_entry' require 'chef/chef_fs/file_system/not_found_error' class Chef module ChefFS module FileSystem class RestListDir < BaseFSDir def initialize(name, parent, api_path = nil, data_handler = nil) super(name, parent) @api_path = api_path || (parent.api_path == "" ? name : "#{parent.api_path}/#{name}") @data_handler = data_handler end attr_reader :api_path attr_reader :data_handler def child(name) result = @children.select { |child| child.name == name }.first if @children result ||= can_have_child?(name, false) ? _make_child_entry(name) : NonexistentFSObject.new(name, self) end def can_have_child?(name, is_dir) name =~ /\.json$/ && !is_dir end def children begin @children ||= root.get_json(api_path).keys.sort.map do |key| _make_child_entry("#{key}.json", true) end rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "Timeout retrieving children: #{e}" rescue Net::HTTPServerException => e if $!.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "HTTP error retrieving children: #{e}" end end end def create_child(name, file_contents) begin object = Chef::JSONCompat.parse(file_contents) rescue Chef::Exceptions::JSON::ParseError => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Parse error reading JSON creating child '#{name}': #{e}" end result = _make_child_entry(name, true) if data_handler object = data_handler.normalize_for_post(object, result) data_handler.verify_integrity(object, result) do |error| raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self), "Error creating '#{name}': #{error}" end end begin rest.post(api_path, object) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Timeout creating '#{name}': #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) elsif $!.response.code == "409" raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self, e), "Failure creating '#{name}': #{path}/#{name} already exists" else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Failure creating '#{name}': #{e.message}" end end @children = nil result end def org parent.org end def environment parent.environment end def rest parent.rest end def _make_child_entry(name, exists = nil) RestListEntry.new(name, self, exists) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/base_fs_object.rb0000644000004100000410000001456112520074675023421 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/path_utils' require 'chef/chef_fs/file_system/operation_not_allowed_error' class Chef module ChefFS module FileSystem class BaseFSObject def initialize(name, parent) @parent = parent @name = name if parent @path = Chef::ChefFS::PathUtils::join(parent.path, name) else if name != '' raise ArgumentError, "Name of root object must be empty string: was '#{name}' instead" end @path = '/' end end attr_reader :name attr_reader :parent attr_reader :path # Override this if you have a special comparison algorithm that can tell # you whether this entry is the same as another--either a quicker or a # more reliable one. Callers will use this to decide whether to upload, # download or diff an object. # # You should not override this if you're going to do the standard # +self.read == other.read+. If you return +nil+, the caller will call # +other.compare_to(you)+ instead. Give them a chance :) # # ==== Parameters # # * +other+ - the entry to compare to # # ==== Returns # # * +[ are_same, value, other_value ]+ # +are_same+ may be +true+, +false+ or +nil+ (which means "don't know"). # +value+ and +other_value+ must either be the text of +self+ or +other+, # +:none+ (if the entry does not exist or has no value) or +nil+ if the # value was not retrieved. # * +nil+ if a definitive answer cannot be had and nothing was retrieved. # # ==== Example # # are_same, value, other_value = entry.compare_to(other) # if are_same.nil? # are_same, other_value, value = other.compare_to(entry) # end # if are_same.nil? # value = entry.read if value.nil? # other_value = entry.read if other_value.nil? # are_same = (value == other_value) # end def compare_to(other) nil end # Override can_have_child? to report whether a given file *could* be added # to this directory. (Some directories can't have subdirs, some can only have .json # files, etc.) def can_have_child?(name, is_dir) false end # Get a child of this entry with the given name. This MUST always # return a child, even if it is NonexistentFSObject. Overriders should # take caution not to do expensive network requests to get the list of # children to fulfill this request, unless absolutely necessary here; it # is intended as a quick way to traverse a hierarchy. # # For example, knife show /data_bags/x/y.json will call # root.child('data_bags').child('x').child('y.json'), which can then # directly perform a network request to retrieve the y.json data bag. No # network request was necessary to retrieve def child(name) NonexistentFSObject.new(name, self) end # Override children to report your *actual* list of children as an array. def children raise NotFoundError.new(self) if !exists? [] end # Expand this entry into a chef object (Chef::Role, ::Node, etc.) def chef_object raise NotFoundError.new(self) if !exists? nil end # Create a child of this entry with the given name and contents. If # contents is nil, create a directory. # # NOTE: create_child_from is an optional method that can also be added to # your entry class, and will be called without actually reading the # file_contents. This is used for knife upload /cookbooks/cookbookname. def create_child(name, file_contents) raise NotFoundError.new(self) if !exists? raise OperationNotAllowedError.new(:create_child, self) end # Delete this item, possibly recursively. Entries MUST NOT delete a # directory unless recurse is true. def delete(recurse) raise NotFoundError.new(self) if !exists? raise OperationNotAllowedError.new(:delete, self) end # Ask whether this entry is a directory. If not, it is a file. def dir? false end # Ask whether this entry exists. def exists? true end # Printable path, generally used to distinguish paths in one root from # paths in another. def path_for_printing if parent parent_path = parent.path_for_printing if parent_path == '.' name else Chef::ChefFS::PathUtils::join(parent.path_for_printing, name) end else name end end def root parent ? parent.root : self end # Read the contents of this file entry. def read raise NotFoundError.new(self) if !exists? raise OperationNotAllowedError.new(:read, self) end # Write the contents of this file entry. def write(file_contents) raise NotFoundError.new(self) if !exists? raise OperationNotAllowedError.new(:write, self) end # Important directory attributes: name, parent, path, root # Overridable attributes: dir?, child(name), path_for_printing # Abstract: read, write, delete, children, can_have_child?, create_child, compare_to end # class BaseFsObject end end end require 'chef/chef_fs/file_system/nonexistent_fs_object' chef-12.3.0/lib/chef/chef_fs/file_system/memory_dir.rb0000644000004100000410000000260312520074675022631 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/nonexistent_fs_object' require 'chef/chef_fs/file_system/memory_file' class Chef module ChefFS module FileSystem class MemoryDir < Chef::ChefFS::FileSystem::BaseFSDir def initialize(name, parent) super(name, parent) @children = [] end attr_reader :children def child(name) @children.select { |child| child.name == name }.first || Chef::ChefFS::FileSystem::NonexistentFSObject.new(name, self) end def add_child(child) @children.push(child) end def can_have_child?(name, is_dir) root.cannot_be_in_regex ? (name !~ root.cannot_be_in_regex) : true end def add_file(path, value) path_parts = path.split('/') dir = add_dir(path_parts[0..-2].join('/')) file = MemoryFile.new(path_parts[-1], dir, value) dir.add_child(file) file end def add_dir(path) path_parts = path.split('/') dir = self path_parts.each do |path_part| subdir = dir.child(path_part) if !subdir.exists? subdir = MemoryDir.new(path_part, dir) dir.add_child(subdir) end dir = subdir end dir end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb0000644000004100000410000001605312520074675030177 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/file_system/chef_repository_file_system_acls_dir' require 'chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir' require 'chef/chef_fs/file_system/chef_repository_file_system_data_bags_dir' require 'chef/chef_fs/file_system/chef_repository_file_system_policies_dir' require 'chef/chef_fs/file_system/multiplexed_dir' require 'chef/chef_fs/data_handler/client_data_handler' require 'chef/chef_fs/data_handler/environment_data_handler' require 'chef/chef_fs/data_handler/node_data_handler' require 'chef/chef_fs/data_handler/role_data_handler' require 'chef/chef_fs/data_handler/user_data_handler' require 'chef/chef_fs/data_handler/group_data_handler' require 'chef/chef_fs/data_handler/container_data_handler' class Chef module ChefFS module FileSystem # # Represents the root of a local Chef repository, with directories for # nodes, cookbooks, roles, etc. under it. # class ChefRepositoryFileSystemRootDir < BaseFSDir # # Create a new Chef Repository File System root. # # == Parameters # [child_paths] # A hash of child paths, e.g.: # "nodes" => [ '/var/nodes', '/home/jkeiser/nodes' ], # "roles" => [ '/var/roles' ], # ... # [root_paths] # An array of paths representing the top level, where # +org.json+, +members.json+, and +invites.json+ will be stored. # [chef_config] - a hash of options that looks suspiciously like the ones # stored in Chef::Config, containing at least these keys: # :versioned_cookbooks:: whether to include versions in cookbook names def initialize(child_paths, root_paths=[], chef_config=Chef::Config) super("", nil) @child_paths = child_paths @root_paths = root_paths @versioned_cookbooks = chef_config[:versioned_cookbooks] end attr_accessor :write_pretty_json attr_reader :root_paths attr_reader :child_paths attr_reader :versioned_cookbooks CHILDREN = %w(invitations.json members.json org.json) def children @children ||= begin result = child_paths.keys.sort.map { |name| make_child_entry(name) }.select { |child| !child.nil? } result += root_dir.children.select { |c| CHILDREN.include?(c.name) } if root_dir result.sort_by { |c| c.name } end end def can_have_child?(name, is_dir) if is_dir child_paths.has_key?(name) elsif root_dir CHILDREN.include?(name) else false end end def create_child(name, file_contents = nil) if file_contents child = root_dir.create_child(name, file_contents) else child_paths[name].each do |path| begin Dir.mkdir(path) rescue Errno::EEXIST end end child = make_child_entry(name) end @children = nil child end def json_class nil end # Used to print out a human-readable file system description def fs_description repo_paths = root_paths || [ File.dirname(child_paths['cookbooks'][0]) ] result = "repository at #{repo_paths.join(', ')}\n" if versioned_cookbooks result << " Multiple versions per cookbook\n" else result << " One version per cookbook\n" end child_paths.each_pair do |name, paths| if paths.any? { |path| !repo_paths.include?(File.dirname(path)) } result << " #{name} at #{paths.join(', ')}\n" end end result end private # # A FileSystemEntry representing the root path where invites.json, # members.json and org.json may be found. # def root_dir existing_paths = root_paths.select { |path| File.exists?(path) } if existing_paths.size > 0 MultiplexedDir.new(existing_paths.map do |path| dir = ChefRepositoryFileSystemEntry.new(name, parent, path) dir.write_pretty_json = !!write_pretty_json dir end) end end # # Create a child entry of the appropriate type: # cookbooks, data_bags, acls, etc. All will be multiplexed (i.e. if # you have multiple paths for cookbooks, the multiplexed dir will grab # cookbooks from all of them when you list or grab them). # def make_child_entry(name) paths = child_paths[name].select do |path| File.exists?(path) end if paths.size == 0 return nil end if name == 'cookbooks' dirs = paths.map { |path| ChefRepositoryFileSystemCookbooksDir.new(name, self, path) } elsif name == 'data_bags' dirs = paths.map { |path| ChefRepositoryFileSystemDataBagsDir.new(name, self, path) } elsif name == 'policies' dirs = paths.map { |path| ChefRepositoryFileSystemPoliciesDir.new(name, self, path) } elsif name == 'acls' dirs = paths.map { |path| ChefRepositoryFileSystemAclsDir.new(name, self, path) } else data_handler = case name when 'clients' Chef::ChefFS::DataHandler::ClientDataHandler.new when 'environments' Chef::ChefFS::DataHandler::EnvironmentDataHandler.new when 'nodes' Chef::ChefFS::DataHandler::NodeDataHandler.new when 'roles' Chef::ChefFS::DataHandler::RoleDataHandler.new when 'users' Chef::ChefFS::DataHandler::UserDataHandler.new when 'groups' Chef::ChefFS::DataHandler::GroupDataHandler.new when 'containers' Chef::ChefFS::DataHandler::ContainerDataHandler.new else raise "Unknown top level path #{name}" end dirs = paths.map { |path| ChefRepositoryFileSystemEntry.new(name, self, path, data_handler) } end MultiplexedDir.new(dirs) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/already_exists_error.rb0000644000004100000410000000172612520074675024721 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/operation_failed_error' class Chef module ChefFS module FileSystem class AlreadyExistsError < OperationFailedError def initialize(operation, entry, cause = nil) super(operation, entry, cause) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/multiplexed_dir.rb0000644000004100000410000000257312520074675023663 0ustar www-datawww-datarequire 'chef/chef_fs/file_system/base_fs_object' require 'chef/chef_fs/file_system/nonexistent_fs_object' class Chef module ChefFS module FileSystem class MultiplexedDir < BaseFSDir def initialize(*multiplexed_dirs) @multiplexed_dirs = multiplexed_dirs.flatten super(@multiplexed_dirs[0].name, @multiplexed_dirs[0].parent) end attr_reader :multiplexed_dirs def write_dir multiplexed_dirs[0] end def children begin result = [] seen = {} # If multiple things have the same name, the first one wins. multiplexed_dirs.each do |dir| dir.children.each do |child| if seen[child.name] Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}") else result << child seen[child.name] = child end end end result end end def can_have_child?(name, is_dir) write_dir.can_have_child?(name, is_dir) end def create_child(name, file_contents = nil) @children = nil write_dir.create_child(name, file_contents) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/not_found_error.rb0000644000004100000410000000166112520074675023672 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/file_system_error' class Chef module ChefFS module FileSystem class NotFoundError < FileSystemError def initialize(entry, cause = nil) super(entry, cause) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir.rb0000644000004100000410000001024612520074675031020 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_cookbook_entry' require 'chef/chef_fs/file_system/cookbook_dir' require 'chef/chef_fs/file_system/not_found_error' require 'chef/cookbook/chefignore' require 'chef/cookbook/cookbook_version_loader' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemCookbookDir < ChefRepositoryFileSystemCookbookEntry def initialize(name, parent, file_path = nil) super(name, parent, file_path) end def chef_object begin loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore) # We need the canonical cookbook name if we are using versioned cookbooks, but we don't # want to spend a lot of time adding code to the main Chef libraries if root.versioned_cookbooks canonical_name = canonical_cookbook_name(File.basename(file_path)) fail "When versioned_cookbooks mode is on, cookbook #{file_path} must match format -x.y.z" unless canonical_name # KLUDGE: We shouldn't have to use instance_variable_set loader.instance_variable_set(:@cookbook_name, canonical_name) end loader.load_cookbooks cb = loader.cookbook_version if !cb Chef::Log.error("Cookbook #{file_path} empty.") raise "Cookbook #{file_path} empty." end cb rescue => e Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{e}") Chef::Log.error(e.backtrace.join("\n")) raise end end def children begin Dir.entries(file_path).sort. select { |child_name| can_have_child?(child_name, File.directory?(File.join(file_path, child_name))) }. map { |child_name| make_child(child_name) }. select { |entry| !(entry.dir? && entry.children.size == 0) } rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end def can_have_child?(name, is_dir) if is_dir # Only the given directories will be uploaded. return CookbookDir::COOKBOOK_SEGMENT_INFO.keys.include?(name.to_sym) && name != 'root_files' elsif name == Chef::Cookbook::CookbookVersionLoader::UPLOADED_COOKBOOK_VERSION_FILE return false end super(name, is_dir) end # Exposed as a class method so that it can be used elsewhere def self.canonical_cookbook_name(entry_name) name_match = Chef::ChefFS::FileSystem::CookbookDir::VALID_VERSIONED_COOKBOOK_NAME.match(entry_name) return nil if name_match.nil? return name_match[1] end def canonical_cookbook_name(entry_name) self.class.canonical_cookbook_name(entry_name) end def uploaded_cookbook_version_path File.join(file_path, Chef::Cookbook::CookbookVersionLoader::UPLOADED_COOKBOOK_VERSION_FILE) end def can_upload? File.exists?(uploaded_cookbook_version_path) || children.size > 0 end protected def make_child(child_name) segment_info = CookbookDir::COOKBOOK_SEGMENT_INFO[child_name.to_sym] || {} ChefRepositoryFileSystemCookbookEntry.new(child_name, self, nil, segment_info[:ruby_only], segment_info[:recursive]) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_entry.rb0000644000004100000410000000555112520074675031406 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir' require 'chef/chef_fs/file_system/not_found_error' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemCookbookEntry < ChefRepositoryFileSystemEntry def initialize(name, parent, file_path = nil, ruby_only = false, recursive = false) super(name, parent, file_path) @ruby_only = ruby_only @recursive = recursive end attr_reader :ruby_only attr_reader :recursive def children begin Dir.entries(file_path).sort. select { |child_name| can_have_child?(child_name, File.directory?(File.join(file_path, child_name))) }. map { |child_name| make_child(child_name) }. select { |entry| !(entry.dir? && entry.children.size == 0) } rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end def can_have_child?(name, is_dir) if is_dir return recursive && name != '.' && name != '..' elsif ruby_only return false if name[-3..-1] != '.rb' end # Check chefignore ignorer = parent loop do if ignorer.is_a?(ChefRepositoryFileSystemCookbooksDir) # Grab the path from entry to child path_to_child = name child = self while child.parent != ignorer path_to_child = PathUtils.join(child.name, path_to_child) child = child.parent end # Check whether that relative path is ignored return !ignorer.chefignore || !ignorer.chefignore.ignored?(path_to_child) end ignorer = ignorer.parent break unless ignorer end true end def write_pretty_json false end protected def make_child(child_name) ChefRepositoryFileSystemCookbookEntry.new(child_name, self, nil, ruby_only, recursive) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/cookbook_frozen_error.rb0000644000004100000410000000172312520074675025067 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/already_exists_error' class Chef module ChefFS module FileSystem class CookbookFrozenError < AlreadyExistsError def initialize(operation, entry, cause = nil) super(operation, entry, cause) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/acls_dir.rb0000644000004100000410000000413112520074675022241 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/acl_dir' require 'chef/chef_fs/file_system/cookbooks_acl_dir' require 'chef/chef_fs/file_system/acl_entry' require 'chef/chef_fs/data_handler/acl_data_handler' class Chef module ChefFS module FileSystem class AclsDir < BaseFSDir ENTITY_TYPES = %w(clients containers cookbooks data_bags environments groups nodes roles) # we don't read sandboxes, so we don't read their acls def initialize(parent) super('acls', parent) end def data_handler @data_handler ||= Chef::ChefFS::DataHandler::AclDataHandler.new end def api_path parent.api_path end def can_have_child?(name, is_dir) is_dir ? ENTITY_TYPES.include(name) : name == 'organization.json' end def children if @children.nil? @children = ENTITY_TYPES.map do |entity_type| case entity_type when 'cookbooks' CookbooksAclDir.new(entity_type, self) else AclDir.new(entity_type, self) end end @children << AclEntry.new('organization.json', self, true) # the org acl is retrieved as GET /organizations/ORGNAME/ANYTHINGATALL/_acl end @children end def rest parent.rest end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/file_system_entry.rb0000644000004100000410000000601212520074675024225 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/already_exists_error' require 'chef/chef_fs/file_system/must_delete_recursively_error' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/path_utils' require 'fileutils' class Chef module ChefFS module FileSystem class FileSystemEntry < BaseFSDir def initialize(name, parent, file_path = nil) super(name, parent) @file_path = file_path || "#{parent.file_path}/#{name}" end attr_reader :file_path def path_for_printing file_path end def children begin Dir.entries(file_path).sort.select { |entry| entry != '.' && entry != '..' }.map { |entry| make_child(entry) } rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end def create_child(child_name, file_contents=nil) child = make_child(child_name) if child.exists? raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child) end if file_contents child.write(file_contents) else begin Dir.mkdir(child.file_path) rescue Errno::EEXIST raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child) end end child end def dir? File.directory?(file_path) end def delete(recurse) if dir? if !recurse raise MustDeleteRecursivelyError.new(self, $!) end FileUtils.rm_rf(file_path) else File.delete(file_path) end end def exists? File.exists?(file_path) end def read begin File.open(file_path, "rb") {|f| f.read} rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end def write(content) File.open(file_path, 'wb') do |file| file.write(content) end end protected def make_child(child_name) FileSystemEntry.new(child_name, self) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/rest_list_entry.rb0000644000004100000410000001363112520074675023717 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/base_fs_object' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/operation_failed_error' require 'chef/role' require 'chef/node' require 'chef/json_compat' class Chef module ChefFS module FileSystem class RestListEntry < BaseFSObject def initialize(name, parent, exists = nil) super(name, parent) @exists = exists end def data_handler parent.data_handler end def api_child_name if name.length < 5 || name[-5,5] != ".json" raise "Invalid name #{path}: must end in .json" end name[0,name.length-5] end def api_path "#{parent.api_path}/#{api_child_name}" end def org parent.org end def environment parent.environment end def exists? if @exists.nil? begin @exists = parent.children.any? { |child| child.name == name } rescue Chef::ChefFS::FileSystem::NotFoundError @exists = false end end @exists end def delete(recurse) begin rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" end end end def read Chef::JSONCompat.to_json_pretty(minimize_value(_read_json)) end def _read_json begin # Minimize the value (get rid of defaults) so the results don't look terrible root.get_json(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "Timeout reading: #{e}" rescue Net::HTTPServerException => e if $!.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "HTTP error reading: #{e}" end end end def chef_object # REST will inflate the Chef object using json_class data_handler.json_class.json_create(read) end def minimize_value(value) data_handler.minimize(data_handler.normalize(value, self), self) end def compare_to(other) # TODO this pair of reads can be parallelized # Grab the other value begin other_value_json = other.read rescue Chef::ChefFS::FileSystem::NotFoundError return [ nil, nil, :none ] end # Grab this value begin value = _read_json rescue Chef::ChefFS::FileSystem::NotFoundError return [ false, :none, other_value_json ] end # Minimize (and normalize) both values for easy and beautiful diffs value = minimize_value(value) value_json = Chef::JSONCompat.to_json_pretty(value) begin other_value = Chef::JSONCompat.parse(other_value_json) rescue Chef::Exceptions::JSON::ParseError => e Chef::Log.warn("Parse error reading #{other.path_for_printing} as JSON: #{e}") return [ nil, value_json, other_value_json ] end other_value = minimize_value(other_value) other_value_json = Chef::JSONCompat.to_json_pretty(other_value) [ value == other_value, value_json, other_value_json ] end def rest parent.rest end def write(file_contents) begin object = Chef::JSONCompat.parse(file_contents) rescue Chef::Exceptions::JSON::ParseError => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Parse error reading JSON: #{e}" end if data_handler object = data_handler.normalize_for_put(object, self) data_handler.verify_integrity(object, self) do |error| raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self), "#{error}" end end begin rest.put(api_path, object) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Timeout writing: #{e}" rescue Net::HTTPServerException => e if e.response.code == "404" raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "HTTP error writing: #{e}" end end end def api_error_text(response) begin Chef::JSONCompat.parse(response.body)['error'].join("\n") rescue response.body end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb0000644000004100000410000000567712520074675027531 0ustar www-datawww-data# # Author:: John Keiser () # Author:: Ho-Sheng Hsiao () # 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/chef_fs/file_system/file_system_entry' require 'chef/chef_fs/file_system/not_found_error' class Chef module ChefFS module FileSystem # ChefRepositoryFileSystemEntry works just like FileSystemEntry, # except can inflate Chef objects class ChefRepositoryFileSystemEntry < FileSystemEntry def initialize(name, parent, file_path = nil, data_handler = nil) super(name, parent, file_path) @data_handler = data_handler end def write_pretty_json=(value) @write_pretty_json = value end def write_pretty_json @write_pretty_json.nil? ? root.write_pretty_json : @write_pretty_json end def data_handler @data_handler || parent.data_handler end def chef_object begin return data_handler.chef_object(Chef::JSONCompat.parse(read)) rescue Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{$!}") end nil end def can_have_child?(name, is_dir) !is_dir && name[-5..-1] == '.json' end def write(file_contents) if file_contents && write_pretty_json && name[-5..-1] == '.json' file_contents = minimize(file_contents, self) end super(file_contents) end def minimize(file_contents, entry) object = Chef::JSONCompat.parse(file_contents) object = data_handler.normalize(object, entry) object = data_handler.minimize(object, entry) Chef::JSONCompat.to_json_pretty(object) end def children # Except cookbooks and data bag dirs, all things must be json files begin Dir.entries(file_path).sort. select { |child_name| can_have_child?(child_name, File.directory?(File.join(file_path, child_name))) }. map { |child_name| make_child(child_name) } rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end end protected def make_child(child_name) ChefRepositoryFileSystemEntry.new(child_name, self) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/operation_not_allowed_error.rb0000644000004100000410000000253112520074675026263 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/file_system_error' class Chef module ChefFS module FileSystem class OperationNotAllowedError < FileSystemError def initialize(operation, entry, cause = nil) super(entry, cause) @operation = operation end attr_reader :operation attr_reader :entry def reason case operation when :delete "cannot be deleted" when :write "cannot be updated" when :create_child "cannot have a child created under it" when :read "cannot be read" end end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/cookbooks_acl_dir.rb0000644000004100000410000000302612520074675024131 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/acl_dir' require 'chef/chef_fs/file_system/acl_entry' class Chef module ChefFS module FileSystem class CookbooksAclDir < AclDir # If versioned_cookbooks is on, the list of cookbooks will have versions # in them. But all versions of a cookbook have the same acl, so even if # we have cookbooks/apache2-1.0.0 and cookbooks/apache2-1.1.2, we will # only have one acl: acls/cookbooks/apache2.json. Thus, the list of # children of acls/cookbooks is a unique list of cookbook *names*. def children if @children.nil? names = parent.parent.child(name).children.map { |child| "#{child.cookbook_name}.json" } @children = names.uniq.map { |name| AclEntry.new(name, self, true) } end @children end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb0000644000004100000410000000227712520074675031026 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/data_handler/policy_data_handler' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemPoliciesDir < ChefRepositoryFileSystemEntry def initialize(name, parent, path = nil) super(name, parent, path, Chef::ChefFS::DataHandler::PolicyDataHandler.new) end def can_have_child?(name, is_dir) is_dir && !name.start_with?('.') end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/file_system_root_dir.rb0000644000004100000410000000166212520074675024713 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/file_system_entry' class Chef module ChefFS module FileSystem class FileSystemRootDir < FileSystemEntry def initialize(file_path) super("", nil, file_path) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/must_delete_recursively_error.rb0000644000004100000410000000167612520074675026653 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/file_system_error' class Chef module ChefFS module FileSystem class MustDeleteRecursivelyError < FileSystemError def initialize(entry, cause = nil) super(entry, cause) end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/chef_repository_file_system_data_bags_dir.rb0000644000004100000410000000231112520074675031111 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/chef_repository_file_system_entry' require 'chef/chef_fs/data_handler/data_bag_item_data_handler' class Chef module ChefFS module FileSystem class ChefRepositoryFileSystemDataBagsDir < ChefRepositoryFileSystemEntry def initialize(name, parent, path = nil) super(name, parent, path, Chef::ChefFS::DataHandler::DataBagItemDataHandler.new) end def can_have_child?(name, is_dir) is_dir && !name.start_with?('.') end end end end end chef-12.3.0/lib/chef/chef_fs/file_system/cookbooks_dir.rb0000644000004100000410000001430412520074675023313 0ustar www-datawww-data# # Author:: John Keiser () # 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/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/cookbook_dir' require 'chef/chef_fs/file_system/operation_failed_error' require 'chef/chef_fs/file_system/cookbook_frozen_error' require 'chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir' require 'chef/mixin/file_class' require 'tmpdir' class Chef module ChefFS module FileSystem class CookbooksDir < RestListDir include Chef::Mixin::FileClass def initialize(parent) super("cookbooks", parent) end def child(name) if @children result = self.children.select { |child| child.name == name }.first if result result else NonexistentFSObject.new(name, self) end else CookbookDir.new(name, self) end end def children @children ||= begin if root.versioned_cookbooks result = [] root.get_json("#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks| cookbooks['versions'].each do |cookbook_version| result << CookbookDir.new("#{cookbook_name}-#{cookbook_version['version']}", self, :exists => true) end end else result = root.get_json(api_path).keys.map { |cookbook_name| CookbookDir.new(cookbook_name, self, :exists => true) } end result.sort_by(&:name) end end def create_child_from(other, options = {}) @children = nil upload_cookbook_from(other, options) end def upload_cookbook_from(other, options = {}) root.versioned_cookbooks ? upload_versioned_cookbook(other, options) : upload_unversioned_cookbook(other, options) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Timeout writing: #{e}" rescue Net::HTTPServerException => e case e.response.code when "409" raise Chef::ChefFS::FileSystem::CookbookFrozenError.new(:write, self, e), "Cookbook #{other.name} is frozen" else raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "HTTP error writing: #{e}" end rescue Chef::Exceptions::CookbookFrozen => e raise Chef::ChefFS::FileSystem::CookbookFrozenError.new(:write, self, e), "Cookbook #{other.name} is frozen" end # Knife currently does not understand versioned cookbooks # Cookbook Version uploader also requires a lot of refactoring # to make this work. So instead, we make a temporary cookbook # symlinking back to real cookbook, and upload the proxy. def upload_versioned_cookbook(other, options) cookbook_name = Chef::ChefFS::FileSystem::ChefRepositoryFileSystemCookbookDir.canonical_cookbook_name(other.name) Dir.mktmpdir do |temp_cookbooks_path| proxy_cookbook_path = "#{temp_cookbooks_path}/#{cookbook_name}" # Make a symlink file_class.symlink other.file_path, proxy_cookbook_path # Instantiate a proxy loader using the temporary symlink proxy_loader = Chef::Cookbook::CookbookVersionLoader.new(proxy_cookbook_path, other.parent.chefignore) proxy_loader.load_cookbooks cookbook_to_upload = proxy_loader.cookbook_version cookbook_to_upload.freeze_version if options[:freeze] # Instantiate a new uploader based on the proxy loader uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(temp_cookbooks_path) do upload_cookbook!(uploader) end # # When the temporary directory is being deleted on # windows, the contents of the symlink under that # directory is also deleted. So explicitly remove # the symlink without removing the original contents if we # are running on windows # if Chef::Platform.windows? Dir.rmdir proxy_cookbook_path end end end def upload_unversioned_cookbook(other, options) cookbook_to_upload = other.chef_object cookbook_to_upload.freeze_version if options[:freeze] uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(other.parent.file_path) do upload_cookbook!(uploader) end end # Work around the fact that CookbookUploader doesn't understand chef_repo_path (yet) def with_actual_cookbooks_dir(actual_cookbook_path) old_cookbook_path = Chef::Config.cookbook_path Chef::Config.cookbook_path = actual_cookbook_path if !Chef::Config.cookbook_path yield ensure Chef::Config.cookbook_path = old_cookbook_path end def upload_cookbook!(uploader, options = {}) if uploader.respond_to?(:upload_cookbook) uploader.upload_cookbook else uploader.upload_cookbooks end end def can_have_child?(name, is_dir) return false if !is_dir return false if root.versioned_cookbooks && name !~ Chef::ChefFS::FileSystem::CookbookDir::VALID_VERSIONED_COOKBOOK_NAME return true end end end end end chef-12.3.0/lib/chef/chef_fs/chef_fs_data_store.rb0000644000004100000410000004172412520074675021751 0ustar www-datawww-data# # Author:: John Keiser () # 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/cookbook_manifest' require 'chef_zero/data_store/memory_store' require 'chef_zero/data_store/data_already_exists_error' require 'chef_zero/data_store/data_not_found_error' require 'chef/chef_fs/file_pattern' require 'chef/chef_fs/file_system' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/memory_root' require 'fileutils' class Chef module ChefFS # # Translation layer between chef-zero's DataStore (a place where it expects # files to be stored) and ChefFS (the user's repository directory layout). # # chef-zero expects the data store to store files *its* way--for example, it # expects get("nodes/blah") to return the JSON text for the blah node, and # it expects get("cookbooks/blah/1.0.0") to return the JSON definition of # the blah cookbook version 1.0.0. # # The repository is defined the way the *user* wants their layout. These # two things are very similar in layout (for example, nodes are stored under # the nodes/ directory and their filename is the name of the node). # # However, there are a few differences that make this more than just a raw # file store: # # 1. Cookbooks are stored much differently. # - chef-zero places JSON text with the checksums for the cookbook at # /cookbooks/NAME/VERSION, and expects the JSON to contain URLs to the # actual files, which are stored elsewhere. # - The repository contains an actual directory with just the cookbook # files and a metadata.rb containing a version #. There is no JSON to # be found. # - Further, if versioned_cookbooks is false, that directory is named # /cookbooks/NAME and only one version exists. If versioned_cookbooks # is true, the directory is named /cookbooks/NAME-VERSION. # - Therefore, ChefFSDataStore calculates the cookbook JSON by looking at # the files in the cookbook and checksumming them, and reading metadata.rb # for the version and dependency information. # - ChefFSDataStore also modifies the cookbook file URLs so that they point # to /file_store/repo/ (the path to the actual file under the # repository root). For example, /file_store/repo/apache2/metadata.rb or # /file_store/repo/cookbooks/apache2/recipes/default.rb). # # 2. Sandboxes don't exist in the repository. # - ChefFSDataStore lets cookbooks be uploaded into a temporary memory # storage, and when the cookbook is committed, copies the files onto the # disk in the correct place (/cookbooks/apache2/recipes/default.rb). # 3. Data bags: # - The Chef server expects data bags in /data/BAG/ITEM # - The repository stores data bags in /data_bags/BAG/ITEM # # 4. JSON filenames are generally NAME.json in the repository (e.g. /nodes/foo.json). # class ChefFSDataStore # # Create a new ChefFSDataStore # # ==== Arguments # # [chef_fs] # A +ChefFS::FileSystem+ object representing the repository root. # Generally will be a +ChefFS::FileSystem::ChefRepositoryFileSystemRoot+ # object, created from +ChefFS::Config.local_fs+. # def initialize(chef_fs) @chef_fs = chef_fs @memory_store = ChefZero::DataStore::MemoryStore.new end def publish_description "Reading and writing data to #{chef_fs.fs_description}" end attr_reader :chef_fs def create_dir(path, name, *options) if use_memory_store?(path) @memory_store.create_dir(path, name, *options) else with_dir(path) do |parent| begin parent.create_child(chef_fs_filename(path + [name]), nil) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end end end end def create(path, name, data, *options) if use_memory_store?(path) @memory_store.create(path, name, data, *options) elsif path[0] == 'cookbooks' && path.length == 2 # Do nothing. The entry gets created when the cookbook is created. else if !data.is_a?(String) raise "set only works with strings" end with_dir(path) do |parent| begin parent.create_child(chef_fs_filename(path + [name]), data) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end end end end def get(path, request=nil) if use_memory_store?(path) @memory_store.get(path) elsif path[0] == 'file_store' && path[1] == 'repo' entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join('/')) begin entry.read rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end else with_entry(path) do |entry| if path[0] == 'cookbooks' && path.length == 3 # get /cookbooks/NAME/version result = nil begin result = Chef::CookbookManifest.new(entry.chef_object).to_hash rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end result.each_pair do |key, value| if value.is_a?(Array) value.each do |file| if file.is_a?(Hash) && file.has_key?('checksum') relative = ['file_store', 'repo', 'cookbooks'] if chef_fs.versioned_cookbooks relative << "#{path[1]}-#{path[2]}" else relative << path[1] end relative = relative + file[:path].split('/') file['url'] = ChefZero::RestBase::build_uri(request.base_uri, relative) end end end end Chef::JSONCompat.to_json_pretty(result) else begin entry.read rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end end def set(path, data, *options) if use_memory_store?(path) @memory_store.set(path, data, *options) else if !data.is_a?(String) raise "set only works with strings: #{path} = #{data.inspect}" end # Write out the files! if path[0] == 'cookbooks' && path.length == 3 write_cookbook(path, data, *options) else with_dir(path[0..-2]) do |parent| child = parent.child(chef_fs_filename(path)) if child.exists? child.write(data) else parent.create_child(chef_fs_filename(path), data) end end end end end def delete(path) if use_memory_store?(path) @memory_store.delete(path) else with_entry(path) do |entry| begin if path[0] == 'cookbooks' && path.length >= 3 entry.delete(true) else entry.delete(false) end rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end def delete_dir(path, *options) if use_memory_store?(path) @memory_store.delete_dir(path, *options) else with_entry(path) do |entry| begin entry.delete(options.include?(:recursive)) rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end def list(path) if use_memory_store?(path) @memory_store.list(path) elsif path[0] == 'cookbooks' && path.length == 1 with_entry(path) do |entry| begin if chef_fs.versioned_cookbooks # /cookbooks/name-version -> /cookbooks/name entry.children.map { |child| split_name_version(child.name)[0] }.uniq else entry.children.map { |child| child.name } end rescue Chef::ChefFS::FileSystem::NotFoundError # If the cookbooks dir doesn't exist, we have no cookbooks (not 404) [] end end elsif path[0] == 'cookbooks' && path.length == 2 if chef_fs.versioned_cookbooks result = with_entry([ 'cookbooks' ]) do |entry| # list /cookbooks/name = filter /cookbooks/name-version down to name entry.children.map { |child| split_name_version(child.name) }. select { |name, version| name == path[1] }. map { |name, version| version } end if result.empty? raise ChefZero::DataStore::DataNotFoundError.new(path) end result else # list /cookbooks/name = version = get_single_cookbook_version(path) [version] end else with_entry(path) do |entry| begin entry.children.map { |c| zero_filename(c) }.sort rescue Chef::ChefFS::FileSystem::NotFoundError => e # /cookbooks, /data, etc. never return 404 if path_always_exists?(path) [] else raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end end def exists?(path) if use_memory_store?(path) @memory_store.exists?(path) else path_always_exists?(path) || Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end end def exists_dir?(path) if use_memory_store?(path) @memory_store.exists_dir?(path) elsif path[0] == 'cookbooks' && path.length == 2 list([ path[0] ]).include?(path[1]) else Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end end private def use_memory_store?(path) return path[0] == 'sandboxes' || path[0] == 'file_store' && path[1] == 'checksums' || path == [ 'environments', '_default' ] end def write_cookbook(path, data, *options) if chef_fs.versioned_cookbooks cookbook_path = File.join('cookbooks', "#{path[1]}-#{path[2]}") else cookbook_path = File.join('cookbooks', path[1]) end # Create a little Chef::ChefFS memory filesystem with the data cookbook_fs = Chef::ChefFS::FileSystem::MemoryRoot.new('uploading') cookbook = Chef::JSONCompat.parse(data) cookbook.each_pair do |key, value| if value.is_a?(Array) value.each do |file| if file.is_a?(Hash) && file.has_key?('checksum') file_data = @memory_store.get(['file_store', 'checksums', file['checksum']]) cookbook_fs.add_file(File.join(cookbook_path, file['path']), file_data) end end end end # Create the .uploaded-cookbook-version.json cookbooks = chef_fs.child('cookbooks') if !cookbooks.exists? cookbooks = chef_fs.create_child('cookbooks') end # We are calling a cookbooks-specific API, so get multiplexed_dirs out of the way if it is there if cookbooks.respond_to?(:multiplexed_dirs) cookbooks = cookbooks.write_dir end cookbooks.write_cookbook(cookbook_path, data, cookbook_fs) end def split_name_version(entry_name) name_version = entry_name.split('-') name = name_version[0..-2].join('-') version = name_version[-1] [name,version] end def to_chef_fs_path(path) _to_chef_fs_path(path).join('/') end def chef_fs_filename(path) _to_chef_fs_path(path)[-1] end def _to_chef_fs_path(path) if path[0] == 'data' path = path.dup path[0] = 'data_bags' if path.length >= 3 path[2] = "#{path[2]}.json" end elsif path[0] == 'policies' path = path.dup if path.length >= 3 path[2] = "#{path[2]}.json" end elsif path[0] == 'cookbooks' if path.length == 2 raise ChefZero::DataStore::DataNotFoundError.new(path) elsif chef_fs.versioned_cookbooks if path.length >= 3 # cookbooks/name/version -> cookbooks/name-version path = [ path[0], "#{path[1]}-#{path[2]}" ] + path[3..-1] end else if path.length >= 3 # cookbooks/name/version/... -> /cookbooks/name/... iff metadata says so version = get_single_cookbook_version(path) if path[2] == version path = path[0..1] + path[3..-1] else raise ChefZero::DataStore::DataNotFoundError.new(path) end end end elsif path.length == 2 path = path.dup path[1] = "#{path[1]}.json" end path end def to_zero_path(entry) path = entry.path.split('/')[1..-1] if path[0] == 'data_bags' path = path.dup path[0] = 'data' if path.length >= 3 path[2] = path[2][0..-6] end elsif path[0] == 'cookbooks' if chef_fs.versioned_cookbooks # cookbooks/name-version/... -> cookbooks/name/version/... if path.length >= 2 name, version = split_name_version(path[1]) path = [ path[0], name, version ] + path[2..-1] end else if path.length >= 2 # cookbooks/name/... -> cookbooks/name/version/... version = get_single_cookbook_version(path) path = path[0..1] + [version] + path[2..-1] end end elsif path.length == 2 && path[0] != 'cookbooks' path = path.dup path[1] = path[1][0..-6] end path end def zero_filename(entry) to_zero_path(entry)[-1] end def path_always_exists?(path) return path.length == 1 && %w(clients cookbooks data environments nodes roles users).include?(path[0]) end def with_entry(path) begin yield Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)) rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end def with_dir(path) # Do not automatically create data bags create = !(path[0] == 'data' && path.size >= 2) begin yield get_dir(_to_chef_fs_path(path), create) rescue Chef::ChefFS::FileSystem::NotFoundError => e err = ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) err.set_backtrace(e.backtrace) raise err end end def get_dir(path, create=false) result = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path.join('/')) if result.exists? result elsif create get_dir(path[0..-2], create).create_child(result.name, nil) else raise ChefZero::DataStore::DataNotFoundError.new(path) end end def get_single_cookbook_version(path) dir = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[0..1].join('/')) metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, []) metadata[:version] || '0.0.0' end end end end chef-12.3.0/lib/chef/chef_fs/parallelizer.rb0000644000004100000410000000526712520074675020637 0ustar www-datawww-datarequire 'thread' require 'chef/chef_fs/parallelizer/parallel_enumerable' class Chef module ChefFS # Tries to balance several guarantees, in order of priority: # - don't get deadlocked # - provide results in desired order # - provide results as soon as they are available # - process input as soon as possible class Parallelizer @@parallelizer = nil @@threads = 0 def self.threads=(value) @@threads = value @@parallelizer.resize(value) if @@parallelizer end def self.parallelizer @@parallelizer ||= Parallelizer.new(@@threads) end def self.parallelize(enumerable, options = {}, &block) parallelizer.parallelize(enumerable, options, &block) end def self.parallel_do(enumerable, options = {}, &block) parallelizer.parallel_do(enumerable, options, &block) end def initialize(num_threads) @tasks = Queue.new @threads = [] @stop_thread = {} resize(num_threads) end def num_threads @threads.size end def parallelize(enumerable, options = {}, &block) ParallelEnumerable.new(@tasks, enumerable, options, &block) end def parallel_do(enumerable, options = {}, &block) ParallelEnumerable.new(@tasks, enumerable, options.merge(:ordered => false), &block).wait end def stop(wait = true, timeout = nil) resize(0, wait, timeout) end def resize(to_threads, wait = true, timeout = nil) if to_threads < num_threads threads_to_stop = @threads[to_threads..num_threads-1] @threads = @threads.slice(0, to_threads) threads_to_stop.each do |thread| @stop_thread[thread] = true end if wait start_time = Time.now threads_to_stop.each do |thread| thread_timeout = timeout ? timeout - (Time.now - start_time) : nil thread.join(thread_timeout) end end else num_threads.upto(to_threads - 1) do |i| @threads[i] = Thread.new(&method(:worker_loop)) end end end def kill @threads.each do |thread| Thread.kill(thread) @stop_thread.delete(thread) end @threads = [] end private def worker_loop begin while !@stop_thread[Thread.current] begin task = @tasks.pop task.call rescue puts "ERROR #{$!}" puts $!.backtrace end end ensure @stop_thread.delete(Thread.current) end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/0000755000004100000410000000000012520074675020220 5ustar www-datawww-datachef-12.3.0/lib/chef/chef_fs/data_handler/client_data_handler.rb0000644000004100000410000000170012520074675024507 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/api_client' class Chef module ChefFS module DataHandler class ClientDataHandler < DataHandlerBase def normalize(client, entry) defaults = { 'name' => remove_dot_json(entry.name), 'clientname' => remove_dot_json(entry.name), 'admin' => false, 'validator' => false, 'chef_type' => 'client' } if entry.respond_to?(:org) && entry.org defaults['orgname'] = entry.org end result = normalize_hash(client, defaults) # You can NOT send json_class, or it will fail result.delete('json_class') result end def preserve_key?(key) return key == 'name' end def chef_class Chef::ApiClient end # There is no Ruby API for Chef::ApiClient end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/acl_data_handler.rb0000644000004100000410000000137212520074675023775 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class AclDataHandler < DataHandlerBase def normalize(acl, entry) # Normalize the order of the keys for easier reading result = normalize_hash(acl, { 'create' => {}, 'read' => {}, 'update' => {}, 'delete' => {}, 'grant' => {} }) result.keys.each do |key| result[key] = normalize_hash(result[key], { 'actors' => [], 'groups' => [] }) result[key]['actors'] = result[key]['actors'].sort result[key]['groups'] = result[key]['groups'].sort end result end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/data_handler_base.rb0000644000004100000410000001436712520074675024160 0ustar www-datawww-dataclass Chef module ChefFS module DataHandler # # The base class for all *DataHandlers. # # DataHandlers' job is to know the innards of Chef objects and manipulate # JSON for them, adding defaults and formatting them. # class DataHandlerBase # # Remove all default values from a Chef object's JSON so that the only # thing you see are the values that have been explicitly set. # Achieves this by calling normalize({}, entry) to get the list of # defaults, and subtracting anything that is the same. # def minimize(object, entry) default_object = default(entry) object.each_pair do |key, value| if default_object[key] == value && !preserve_key?(key) object.delete(key) end end object end # # Takes a name like blah.json and removes the .json from it. # def remove_dot_json(name) if name.length < 5 || name[-5,5] != ".json" raise "Invalid name #{path}: must end in .json" end name[0,name.length-5] end # # Return true if minimize() should preserve a key even if it is the same # as the default. Often used for ids and names. # def preserve_key?(key) false end # # Get the default value for an entry. Calls normalize({}, entry). # def default(entry) normalize({}, entry) end # # Utility function to help subclasses do normalize(). Pass in a hash # and a list of keys with defaults, and normalize will: # # 1. Fill in the defaults # 2. Put the actual values in the order of the defaults # 3. Move any other values to the end # # == Example # # normalize_hash({x: 100, c: 2, a: 1}, { a: 10, b: 20, c: 30}) # -> { a: 1, b: 20, c: 2, x: 100} # def normalize_hash(object, defaults) # Make a normalized result in the specified order for diffing result = {} defaults.each_pair do |key, default| result[key] = object.has_key?(key) ? object[key] : default end object.each_pair do |key, value| result[key] = value if !result.has_key?(key) end result end # Specialized function to normalize an object before POSTing it, since # some object types want slightly different values on POST. # If not overridden, this just calls normalize() def normalize_for_post(object, entry) normalize(object, entry) end # Specialized function to normalize an object before PUTing it, since # some object types want slightly different values on PUT. # If not overridden, this just calls normalize(). def normalize_for_put(object, entry) normalize(object, entry) end # # normalize a run list (an array of run list items). # Leaves recipe[name] and role[name] alone, and translates # name to recipe[name]. Then calls uniq on the result. # def normalize_run_list(run_list) run_list.map{|item| case item.to_s when /^recipe\[.*\]$/ item # explicit recipe when /^role\[.*\]$/ item # explicit role else "recipe[#{item}]" end }.uniq end # # Bring in an instance of this object from Ruby. (Like roles/x.rb) # def from_ruby(ruby) chef_class.from_file(ruby).to_hash end # # Turn a JSON hash into a bona fide Chef object (like Chef::Node). # def chef_object(object) chef_class.json_create(object) end # # Write out the Ruby file for this instance. (Like roles/x.rb) # def to_ruby(object) raise NotImplementedError end # # Get the class for instances of this type. Must be overridden. # def chef_class raise NotImplementedError end # # Helper to write out a Ruby file for a JSON hash. Writes out only # the keys specified in "keys"; anything else must be emitted by the # caller. # # == Example # # to_ruby_keys({"name" => "foo", "environment" => "desert", "foo": "bar"}, [ "name", "environment" ]) # -> # 'name "foo" # environment "desert"' # def to_ruby_keys(object, keys) result = '' keys.each do |key| if object[key] if object[key].is_a?(Hash) if object[key].size > 0 result << key first = true object[key].each_pair do |k,v| if first first = false else result << ' '*key.length end result << " #{k.inspect} => #{v.inspect}\n" end end elsif object[key].is_a?(Array) if object[key].size > 0 result << key first = true object[key].each do |value| if first first = false else result << ", " end result << value.inspect end result << "\n" end elsif !object[key].nil? result << "#{key} #{object[key].inspect}\n" end end end result end # # Verify that the JSON hash for this type has a key that matches its name. # Calls the on_error block with the error, if there is one. # def verify_integrity(object, entry, &on_error) base_name = remove_dot_json(entry.name) if object['name'] != base_name on_error.call("Name must be '#{base_name}' (is '#{object['name']}')") end end end # class DataHandlerBase end end end chef-12.3.0/lib/chef/chef_fs/data_handler/cookbook_data_handler.rb0000644000004100000410000000203712520074675025043 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/cookbook/metadata' class Chef module ChefFS module DataHandler class CookbookDataHandler < DataHandlerBase def normalize(cookbook, entry) version = entry.name name = entry.parent.name result = normalize_hash(cookbook, { 'name' => "#{name}-#{version}", 'version' => version, 'cookbook_name' => name, 'json_class' => 'Chef::CookbookVersion', 'chef_type' => 'cookbook_version', 'frozen?' => false, 'metadata' => {} }) result['metadata'] = normalize_hash(result['metadata'], { 'version' => version, 'name' => name }) end def preserve_key?(key) return key == 'cookbook_name' || key == 'version' end def chef_class Chef::Cookbook::Metadata end # Not using this yet, so not sure if to_ruby will be useful. end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/role_data_handler.rb0000644000004100000410000000215312520074675024175 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/role' class Chef module ChefFS module DataHandler class RoleDataHandler < DataHandlerBase def normalize(role, entry) result = normalize_hash(role, { 'name' => remove_dot_json(entry.name), 'description' => '', 'json_class' => 'Chef::Role', 'chef_type' => 'role', 'default_attributes' => {}, 'override_attributes' => {}, 'run_list' => [], 'env_run_lists' => {} }) result['run_list'] = normalize_run_list(result['run_list']) result['env_run_lists'].each_pair do |env, run_list| result['env_run_lists'][env] = normalize_run_list(run_list) end result end def preserve_key?(key) return key == 'name' end def chef_class Chef::Role end def to_ruby(object) to_ruby_keys(object, %w(name description default_attributes override_attributes run_list env_run_lists)) end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/organization_members_data_handler.rb0000644000004100000410000000063612520074675027456 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class OrganizationMembersDataHandler < DataHandlerBase def normalize(members, entry) members.map { |member| member.is_a?(Hash) ? member['user']['username'] : member }.sort.uniq end def minimize(members, entry) members end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/environment_data_handler.rb0000644000004100000410000000214312520074675025577 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/environment' class Chef module ChefFS module DataHandler class EnvironmentDataHandler < DataHandlerBase def normalize(environment, entry) normalize_hash(environment, { 'name' => remove_dot_json(entry.name), 'description' => '', 'cookbook_versions' => {}, 'default_attributes' => {}, 'override_attributes' => {}, 'json_class' => 'Chef::Environment', 'chef_type' => 'environment' }) end def preserve_key?(key) return key == 'name' end def chef_class Chef::Environment end def to_ruby(object) result = to_ruby_keys(object, %w(name description default_attributes override_attributes)) if object['cookbook_versions'] object['cookbook_versions'].each_pair do |name, version| result << "cookbook #{name.inspect}, #{version.inspect}" end end result end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/node_data_handler.rb0000644000004100000410000000156312520074675024165 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/node' class Chef module ChefFS module DataHandler class NodeDataHandler < DataHandlerBase def normalize(node, entry) result = normalize_hash(node, { 'name' => remove_dot_json(entry.name), 'json_class' => 'Chef::Node', 'chef_type' => 'node', 'chef_environment' => '_default', 'override' => {}, 'normal' => {}, 'default' => {}, 'automatic' => {}, 'run_list' => [] }) result['run_list'] = normalize_run_list(result['run_list']) result end def preserve_key?(key) return key == 'name' end def chef_class Chef::Node end # Nodes do not support .rb files end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb0000644000004100000410000000354212520074675025777 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/data_bag_item' class Chef module ChefFS module DataHandler class DataBagItemDataHandler < DataHandlerBase def normalize(data_bag_item, entry) # If it's wrapped with raw_data, unwrap it. if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'] data_bag_item = data_bag_item['raw_data'] end # chef_type and data_bag come back in PUT and POST results, but we don't # use those in knife-essentials. normalize_hash(data_bag_item, { 'id' => remove_dot_json(entry.name) }) end def normalize_for_post(data_bag_item, entry) if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'] data_bag_item = data_bag_item['raw_data'] end { "name" => "data_bag_item_#{entry.parent.name}_#{remove_dot_json(entry.name)}", "json_class" => "Chef::DataBagItem", "chef_type" => "data_bag_item", "data_bag" => entry.parent.name, "raw_data" => normalize(data_bag_item, entry) } end def normalize_for_put(data_bag_item, entry) normalize_for_post(data_bag_item, entry) end def preserve_key?(key) return key == 'id' end def chef_class Chef::DataBagItem end def verify_integrity(object, entry, &on_error) base_name = remove_dot_json(entry.name) if object['raw_data']['id'] != base_name on_error.call("ID in #{entry.path_for_printing} must be '#{base_name}' (is '#{object['raw_data']['id']}')") end end # Data bags do not support .rb files (or if they do, it's undocumented) end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/user_data_handler.rb0000644000004100000410000000142012520074675024206 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class UserDataHandler < DataHandlerBase def normalize(user, entry) normalize_hash(user, { 'name' => remove_dot_json(entry.name), 'username' => remove_dot_json(entry.name), 'display_name' => remove_dot_json(entry.name), 'admin' => false, 'json_class' => 'Chef::WebUIUser', 'chef_type' => 'webui_user', 'salt' => nil, 'password' => nil, 'openid' => nil }) end def preserve_key?(key) return key == 'name' end # There is no chef_class for users, nor does to_ruby work. end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/organization_data_handler.rb0000644000004100000410000000145412520074675025743 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class OrganizationDataHandler < DataHandlerBase def normalize(organization, entry) result = normalize_hash(organization, { 'name' => entry.org, 'full_name' => entry.org, 'org_type' => 'Business', 'clientname' => "#{entry.org}-validator", 'billing_plan' => 'platform-free', }) result end def preserve_key?(key) return key == 'name' end def verify_integrity(object, entry, &on_error) if entry.org != object['name'] on_error.call("Name must be '#{entry.org}' (is '#{object['name']}')") end end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb0000644000004100000410000000062612520074675027504 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class OrganizationInvitesDataHandler < DataHandlerBase def normalize(invites, entry) invites.map { |invite| invite.is_a?(Hash) ? invite['username'] : invite }.sort.uniq end def minimize(invites, entry) invites end end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/group_data_handler.rb0000644000004100000410000000266612520074675024401 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' require 'chef/api_client' class Chef module ChefFS module DataHandler class GroupDataHandler < DataHandlerBase def normalize(group, entry) defaults = { 'name' => remove_dot_json(entry.name), 'groupname' => remove_dot_json(entry.name), 'users' => [], 'clients' => [], 'groups' => [], } if entry.org defaults['orgname'] = entry.org end result = normalize_hash(group, defaults) if result['actors'] && result['actors'].sort.uniq == (result['users'] + result['clients']).sort.uniq result.delete('actors') end result end def normalize_for_put(group, entry) result = super(group, entry) result['actors'] = { 'users' => result['users'], 'clients' => result['clients'], 'groups' => result['groups'] } result.delete('users') result.delete('clients') result.delete('groups') result end def normalize_for_post(group, entry) normalize_for_put(group, entry) end def preserve_key?(key) return key == 'name' end def chef_class Chef::ApiClient end # There is no Ruby API for Chef::ApiClient end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/container_data_handler.rb0000644000004100000410000000152412520074675025217 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class ContainerDataHandler < DataHandlerBase def normalize(container, entry) normalize_hash(container, { 'containername' => remove_dot_json(entry.name), 'containerpath' => remove_dot_json(entry.name) }) end def preserve_key?(key) return key == 'containername' end def verify_integrity(object, entry, &on_error) base_name = remove_dot_json(entry.name) if object['containername'] != base_name on_error.call("Name in #{entry.path_for_printing} must be '#{base_name}' (is '#{object['name']}')") end end # There is no chef_class for users, nor does to_ruby work. end end end end chef-12.3.0/lib/chef/chef_fs/data_handler/policy_data_handler.rb0000644000004100000410000000037112520074675024533 0ustar www-datawww-datarequire 'chef/chef_fs/data_handler/data_handler_base' class Chef module ChefFS module DataHandler class PolicyDataHandler < DataHandlerBase def normalize(policy, entry) policy end end end end end chef-12.3.0/lib/chef/data_bag_item.rb0000644000004100000410000001302412520074675017302 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # Author:: Christopher Brown () # 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 'forwardable' require 'chef/config' require 'chef/mixin/params_validate' require 'chef/mixin/from_file' require 'chef/data_bag' require 'chef/mash' require 'chef/json_compat' class Chef class DataBagItem extend Forwardable include Chef::Mixin::FromFile include Chef::Mixin::ParamsValidate VALID_ID = /^[\.\-[:alnum:]_]+$/ attr_accessor :chef_server_rest def self.validate_id!(id_str) if id_str.nil? || ( id_str !~ VALID_ID ) raise Exceptions::InvalidDataBagItemID, "Data Bag items must have an id matching #{VALID_ID.inspect}, you gave: #{id_str.inspect}" end end # Define all Hash's instance methods as delegating to @raw_data def_delegators(:@raw_data, *(Hash.instance_methods - Object.instance_methods)) attr_reader :raw_data # Create a new Chef::DataBagItem def initialize(chef_server_rest: nil) @data_bag = nil @raw_data = Mash.new @chef_server_rest = chef_server_rest end def chef_server_rest @chef_server_rest ||= Chef::REST.new(Chef::Config[:chef_server_url]) end def self.chef_server_rest Chef::REST.new(Chef::Config[:chef_server_url]) end def raw_data @raw_data end def validate_id!(id_str) self.class.validate_id!(id_str) end def raw_data=(new_data) unless new_data.respond_to?(:[]) && new_data.respond_to?(:keys) raise Exceptions::ValidationFailed, "Data Bag Items must contain a Hash or Mash!" end validate_id!(new_data["id"]) @raw_data = new_data end def data_bag(arg=nil) set_or_return( :data_bag, arg, :regex => /^[\-[:alnum:]_]+$/ ) end def name object_name end def object_name raise Exceptions::ValidationFailed, "You must have an 'id' or :id key in the raw data" unless raw_data.has_key?('id') raise Exceptions::ValidationFailed, "You must have declared what bag this item belongs to!" unless data_bag id = raw_data['id'] "data_bag_item_#{data_bag}_#{id}" end def self.object_name(data_bag_name, id) "data_bag_item_#{data_bag_name}_#{id}" end def to_hash result = self.raw_data result["chef_type"] = "data_bag_item" result["data_bag"] = self.data_bag result end # Serialize this object as a hash def to_json(*a) result = { "name" => object_name, "json_class" => self.class.name, "chef_type" => "data_bag_item", "data_bag" => data_bag, "raw_data" => raw_data } Chef::JSONCompat.to_json(result, *a) end def self.from_hash(h) item = new item.raw_data = h item end # Create a Chef::DataBagItem from JSON def self.json_create(o) bag_item = new bag_item.data_bag(o["data_bag"]) o.delete("data_bag") o.delete("chef_type") o.delete("json_class") o.delete("name") bag_item.raw_data = Mash.new(o["raw_data"]) bag_item end # Load a Data Bag Item by name via either the RESTful API or local data_bag_path if run in solo mode def self.load(data_bag, name) if Chef::Config[:solo] bag = Chef::DataBag.load(data_bag) item = bag[name] else item = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data/#{data_bag}/#{name}") end if item.kind_of?(DataBagItem) item else item = from_hash(item) item.data_bag(data_bag) item end end def destroy(data_bag=data_bag(), databag_item=name) chef_server_rest.delete_rest("data/#{data_bag}/#{databag_item}") end # Save this Data Bag Item via RESTful API def save(item_id=@raw_data['id']) r = chef_server_rest begin if Chef::Config[:why_run] Chef::Log.warn("In whyrun mode, so NOT performing data bag item save.") else r.put_rest("data/#{data_bag}/#{item_id}", self) end rescue Net::HTTPServerException => e raise e unless e.response.code == "404" r.post_rest("data/#{data_bag}", self) end self end # Create this Data Bag Item via RESTful API def create chef_server_rest.post_rest("data/#{data_bag}", self) self end def ==(other) other.respond_to?(:to_hash) && other.respond_to?(:data_bag) && (other.to_hash == to_hash) && (other.data_bag.to_s == data_bag.to_s) end # As a string def to_s "data_bag_item[#{id}]" end def inspect "data_bag_item[#{data_bag.inspect}, #{raw_data['id'].inspect}, #{raw_data.inspect}]" end def pretty_print(pretty_printer) pretty_printer.pp({"data_bag_item('#{data_bag}', '#{id}')" => self.to_hash}) end def id @raw_data['id'] end end end chef-12.3.0/lib/chef/node_map.rb0000644000004100000410000001147112520074675016330 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class NodeMap VALID_OPTS = [ :on_platform, :on_platforms, :platform, :os, :platform_family, ] DEPRECATED_OPTS = [ :on_platform, :on_platforms, ] # Create a new NodeMap # def initialize @map = {} end # Set a key/value pair on the map with a filter. The filter must be true # when applied to the node in order to retrieve the value. # # @param key [Object] Key to store # @param value [Object] Value associated with the key # @param filters [Hash] Node filter options to apply to key retrieval # @yield [node] Arbitrary node filter as a block which takes a node argument # @return [NodeMap] Returns self for possible chaining # def set(key, value, filters = {}, &block) validate_filter!(filters) deprecate_filter!(filters) @map[key] ||= [] # we match on the first value we find, so we want to unshift so that the # last setter wins # FIXME: need a test for this behavior @map[key].unshift({ filters: filters, block: block, value: value }) self end # Get a value from the NodeMap via applying the node to the filters that # were set on the key. # # @param node [Chef::Node] The Chef::Node object for the run # @param key [Object] Key to look up # @return [Object] Value # def get(node, key) # FIXME: real exception raise "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) return nil unless @map.has_key?(key) @map[key].each do |matcher| if filters_match?(node, matcher[:filters]) && block_matches?(node, matcher[:block]) return matcher[:value] end end nil end private # only allow valid filter options def validate_filter!(filters) filters.each_key do |key| # FIXME: real exception raise "Bad key #{key} in Chef::NodeMap filter expression" unless VALID_OPTS.include?(key) end end # warn on deprecated filter options def deprecate_filter!(filters) filters.each_key do |key| Chef::Log.warn "The #{key} option to node_map has been deprecated" if DEPRECATED_OPTS.include?(key) end end # @todo: this works fine, but is probably hard to understand def negative_match(filter, param) # We support strings prefaced by '!' to mean 'not'. In particular, this is most useful # for os matching on '!windows'. negative_matches = filter.select { |f| f[0] == '!' } return true if !negative_matches.empty? && negative_matches.include?('!' + param) # We support the symbol :all to match everything, for backcompat, but this can and should # simply be ommitted. positive_matches = filter.reject { |f| f[0] == '!' || f == :all } return true if !positive_matches.empty? && !positive_matches.include?(param) # sorry double-negative: this means we pass this filter. false end def filters_match?(node, filters) return true if filters.empty? # each filter is applied in turn. if any fail, then it shortcuts and returns false. # if it passes or does not exist it succeeds and continues on. so multiple filters are # effectively joined by 'and'. all filters can be single strings, or arrays which are # effectively joined by 'or'. os_filter = [ filters[:os] ].flatten.compact unless os_filter.empty? return false if negative_match(os_filter, node[:os]) end platform_family_filter = [ filters[:platform_family] ].flatten.compact unless platform_family_filter.empty? return false if negative_match(platform_family_filter, node[:platform_family]) end # :on_platform and :on_platforms here are synonyms which are deprecated platform_filter = [ filters[:platform] || filters[:on_platform] || filters[:on_platforms] ].flatten.compact unless platform_filter.empty? return false if negative_match(platform_filter, node[:platform]) end return true end def block_matches?(node, block) return true if block.nil? block.call node end end end chef-12.3.0/lib/chef/server_api.rb0000644000004100000410000000266012520074675016705 0ustar www-datawww-data# # Author:: John Keiser () # 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/http' require 'chef/http/authenticator' require 'chef/http/cookie_manager' require 'chef/http/decompressor' require 'chef/http/json_input' require 'chef/http/json_output' require 'chef/http/remote_request_id' class Chef class ServerAPI < Chef::HTTP def initialize(url = Chef::Config[:chef_server_url], options = {}) options[:client_name] ||= Chef::Config[:node_name] options[:signing_key_filename] ||= Chef::Config[:client_key] options[:signing_key_filename] = nil if chef_zero_uri?(url) super(url, options) end use Chef::HTTP::JSONInput use Chef::HTTP::JSONOutput use Chef::HTTP::CookieManager use Chef::HTTP::Decompressor use Chef::HTTP::Authenticator use Chef::HTTP::RemoteRequestID end end chef-12.3.0/lib/chef/api_client.rb0000644000004100000410000001355712520074675016664 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # 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 '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 ApiClient include Chef::Mixin::FromFile include Chef::Mixin::ParamsValidate # Create a new Chef::ApiClient object. def initialize @name = '' @public_key = nil @private_key = nil @admin = false @validator = false end # Gets or sets the client name. # # @params [Optional String] The name must be alpha-numeric plus - and _. # @return [String] The current value of the name. def name(arg=nil) set_or_return( :name, arg, :regex => /^[\-[:alnum:]_\.]+$/ ) end # Gets or sets whether this client is an admin. # # @params [Optional True/False] Should be true or false - default is false. # @return [True/False] The current value def admin(arg=nil) set_or_return( :admin, arg, :kind_of => [ TrueClass, FalseClass ] ) end # Gets or sets the public key. # # @params [Optional String] The string representation of the public key. # @return [String] The current value. def public_key(arg=nil) set_or_return( :public_key, arg, :kind_of => String ) end # Gets or sets whether this client is a validator. # # @params [Boolean] whether or not the client is a validator. If # `nil`, retrieves the already-set value. # @return [Boolean] The current value def validator(arg=nil) set_or_return( :validator, arg, :kind_of => [TrueClass, FalseClass] ) end # Gets or sets the private key. # # @params [Optional String] The string representation of the private key. # @return [String] The current value. def private_key(arg=nil) set_or_return( :private_key, arg, :kind_of => [String, FalseClass] ) end # The hash representation of the object. Includes the name and public_key. # Private key is included if available. # # @return [Hash] def to_hash result = { "name" => @name, "public_key" => @public_key, "validator" => @validator, "admin" => @admin, 'json_class' => self.class.name, "chef_type" => "client" } result["private_key"] = @private_key if @private_key result end # The JSON representation of the object. # # @return [String] the JSON string. def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def self.from_hash(o) client = Chef::ApiClient.new client.name(o["name"] || o["clientname"]) client.private_key(o["private_key"]) if o.key?("private_key") client.public_key(o["public_key"]) client.admin(o["admin"]) client.validator(o["validator"]) client end def self.json_create(data) from_hash(data) end def self.from_json(j) from_hash(Chef::JSONCompat.parse(j)) end def self.http_api Chef::REST.new(Chef::Config[:chef_server_url]) end def self.reregister(name) api_client = load(name) api_client.reregister end def self.list(inflate=false) if inflate response = Hash.new Chef::Search::Query.new.search(:client) do |n| n = self.json_create(n) if n.instance_of?(Hash) response[n.name] = n end response else http_api.get("clients") end end # Load a client by name via the API def self.load(name) response = http_api.get("clients/#{name}") if response.kind_of?(Chef::ApiClient) response else json_create(response) end end # Remove this client via the REST API def destroy http_api.delete("clients/#{@name}") end # Save this client via the REST API, returns a hash including the private key def save begin http_api.put("clients/#{name}", { :name => self.name, :admin => self.admin, :validator => self.validator}) rescue Net::HTTPServerException => e # If that fails, go ahead and try and update it if e.response.code == "404" http_api.post("clients", {:name => self.name, :admin => self.admin, :validator => self.validator }) else raise e end end end def reregister reregistered_self = http_api.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true }) if reregistered_self.respond_to?(:[]) private_key(reregistered_self["private_key"]) else private_key(reregistered_self.private_key) end self end # Create the client via the REST API def create http_api.post("clients", self) end # As a string def to_s "client[#{@name}]" end def inspect "Chef::ApiClient name:'#{name}' admin:'#{admin.inspect}' validator:'#{validator}' " + "public_key:'#{public_key}' private_key:'#{private_key}'" end def http_api @http_api ||= Chef::REST.new(Chef::Config[:chef_server_url]) end end end chef-12.3.0/lib/chef/reserved_names.rb0000644000004100000410000000046712520074675017553 0ustar www-datawww-dataclass Chef # This module exists to hide conflicting constant names from the DSL. # Hopefully we'll have a better/prettier/more sustainable solution in the # future, but for now this will fix a regression introduced in Chef 0.10.10 # (conflict with the Win32 namespace) module ReservedNames end end chef-12.3.0/lib/chef/version_class.rb0000644000004100000410000000340012520074675017411 0ustar www-datawww-data# Author:: Seth Falcon () # Author:: Christopher Walters () # Copyright:: Copyright 2010-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. class Chef class Version include Comparable attr_reader :major, :minor, :patch def initialize(str="") parse(str) end def inspect "#{@major}.#{@minor}.#{@patch}" end def to_s "#{@major}.#{@minor}.#{@patch}" end def <=>(v) [:major, :minor, :patch].each do |method| ans = (self.send(method) <=> v.send(method)) return ans if ans != 0 end 0 end def hash # Didn't put any thought or research into this, probably can be # done better to_s.hash end # For hash def eql?(other) other.is_a?(Version) && self == other end protected def parse(str="") @major, @minor, @patch = case str.to_s when /^(\d+)\.(\d+)\.(\d+)$/ [ $1.to_i, $2.to_i, $3.to_i ] when /^(\d+)\.(\d+)$/ [ $1.to_i, $2.to_i, 0 ] else msg = "'#{str.to_s}' does not match 'x.y.z' or 'x.y'" raise Chef::Exceptions::InvalidCookbookVersion.new( msg ) end end end end chef-12.3.0/lib/chef/run_context/0000755000004100000410000000000012520074675016565 5ustar www-datawww-datachef-12.3.0/lib/chef/run_context/cookbook_compiler.rb0000644000004100000410000002527212520074675022622 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'set' require 'chef/log' require 'chef/recipe' require 'chef/resource/lwrp_base' require 'chef/provider/lwrp_base' require 'chef/resource_definition_list' class Chef class RunContext # Implements the compile phase of the chef run by loading/eval-ing files # from cookbooks in the correct order and in the correct context. class CookbookCompiler attr_reader :events attr_reader :run_list_expansion def initialize(run_context, run_list_expansion, events) @run_context = run_context @events = events @run_list_expansion = run_list_expansion @cookbook_order = nil end # Chef::Node object for the current run. def node @run_context.node end # Chef::CookbookCollection object for the current run def cookbook_collection @run_context.cookbook_collection end # Resource Definitions from the compiled cookbooks. This is populated by # calling #compile_resource_definitions (which is called by #compile) def definitions @run_context.definitions end # Run the compile phase of the chef run. Loads files in the following order: # * Libraries # * Attributes # * LWRPs # * Resource Definitions # * Recipes # # Recipes are loaded in precisely the order specified by the expanded run_list. # # Other files are loaded in an order derived from the expanded run_list # and the dependencies declared by cookbooks' metadata. See # #cookbook_order for more information. def compile compile_libraries compile_attributes compile_lwrps compile_resource_definitions compile_recipes end # Extracts the cookbook names from the expanded run list, then iterates # over the list, recursing through dependencies to give a run_list # ordered array of cookbook names with no duplicates. Dependencies appear # before the cookbook(s) that depend on them. def cookbook_order @cookbook_order ||= begin ordered_cookbooks = [] seen_cookbooks = {} run_list_expansion.recipes.each do |recipe| cookbook = Chef::Recipe.parse_recipe_name(recipe).first add_cookbook_with_deps(ordered_cookbooks, seen_cookbooks, cookbook) end Chef::Log.debug("Cookbooks to compile: #{ordered_cookbooks.inspect}") ordered_cookbooks end end # Loads library files from cookbooks according to #cookbook_order. def compile_libraries @events.library_load_start(count_files_by_segment(:libraries)) cookbook_order.each do |cookbook| load_libraries_from_cookbook(cookbook) end @events.library_load_complete end # Loads attributes files from cookbooks. Attributes files are loaded # according to #cookbook_order; within a cookbook, +default.rb+ is loaded # first, then the remaining attributes files in lexical sort order. def compile_attributes @events.attribute_load_start(count_files_by_segment(:attributes)) cookbook_order.each do |cookbook| load_attributes_from_cookbook(cookbook) end @events.attribute_load_complete end # Loads LWRPs according to #cookbook_order. Providers are loaded before # resources on a cookbook-wise basis. def compile_lwrps lwrp_file_count = count_files_by_segment(:providers) + count_files_by_segment(:resources) @events.lwrp_load_start(lwrp_file_count) cookbook_order.each do |cookbook| load_lwrps_from_cookbook(cookbook) end @events.lwrp_load_complete end # Loads resource definitions according to #cookbook_order def compile_resource_definitions @events.definition_load_start(count_files_by_segment(:definitions)) cookbook_order.each do |cookbook| load_resource_definitions_from_cookbook(cookbook) end @events.definition_load_complete end # Iterates over the expanded run_list, loading each recipe in turn. def compile_recipes @events.recipe_load_start(run_list_expansion.recipes.size) run_list_expansion.recipes.each do |recipe| begin @run_context.load_recipe(recipe) rescue Chef::Exceptions::RecipeNotFound => e @events.recipe_not_found(e) raise rescue Exception => e path = resolve_recipe(recipe) @events.recipe_file_load_failed(path, e) raise end end @events.recipe_load_complete end # Whether or not a cookbook is reachable from the set of cookbook given # by the run_list plus those cookbooks' dependencies. def unreachable_cookbook?(cookbook_name) !reachable_cookbooks.include?(cookbook_name) end # All cookbooks in the dependency graph, returned as a Set. def reachable_cookbooks @reachable_cookbooks ||= Set.new(cookbook_order) end private def load_attributes_from_cookbook(cookbook_name) list_of_attr_files = files_in_cookbook_by_segment(cookbook_name, :attributes).dup if default_file = list_of_attr_files.find {|path| File.basename(path) == "default.rb" } list_of_attr_files.delete(default_file) load_attribute_file(cookbook_name.to_s, default_file) end list_of_attr_files.each do |filename| load_attribute_file(cookbook_name.to_s, filename) end end def load_attribute_file(cookbook_name, filename) Chef::Log.debug("Node #{node.name} loading cookbook #{cookbook_name}'s attribute file #{filename}") attr_file_basename = ::File.basename(filename, ".rb") node.include_attribute("#{cookbook_name}::#{attr_file_basename}") rescue Exception => e @events.attribute_file_load_failed(filename, e) raise end def load_libraries_from_cookbook(cookbook_name) files_in_cookbook_by_segment(cookbook_name, :libraries).each do |filename| begin Chef::Log.debug("Loading cookbook #{cookbook_name}'s library file: #{filename}") Kernel.load(filename) @events.library_file_loaded(filename) rescue Exception => e @events.library_file_load_failed(filename, e) raise end end end def load_lwrps_from_cookbook(cookbook_name) files_in_cookbook_by_segment(cookbook_name, :providers).each do |filename| load_lwrp_provider(cookbook_name, filename) end files_in_cookbook_by_segment(cookbook_name, :resources).each do |filename| load_lwrp_resource(cookbook_name, filename) end end def load_lwrp_provider(cookbook_name, filename) Chef::Log.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}") Chef::Provider::LWRPBase.build_from_file(cookbook_name, filename, self) @events.lwrp_file_loaded(filename) rescue Exception => e @events.lwrp_file_load_failed(filename, e) raise end def load_lwrp_resource(cookbook_name, filename) Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}") Chef::Resource::LWRPBase.build_from_file(cookbook_name, filename, self) @events.lwrp_file_loaded(filename) rescue Exception => e @events.lwrp_file_load_failed(filename, e) raise end def load_resource_definitions_from_cookbook(cookbook_name) files_in_cookbook_by_segment(cookbook_name, :definitions).each do |filename| begin Chef::Log.debug("Loading cookbook #{cookbook_name}'s definitions from #{filename}") resourcelist = Chef::ResourceDefinitionList.new resourcelist.from_file(filename) definitions.merge!(resourcelist.defines) do |key, oldval, newval| Chef::Log.info("Overriding duplicate definition #{key}, new definition found in #{filename}") newval end @events.definition_file_loaded(filename) rescue Exception => e @events.definition_file_load_failed(filename, e) raise end end end # Builds up the list of +ordered_cookbooks+ by first recursing through the # dependencies of +cookbook+, and then adding +cookbook+ to the list of # +ordered_cookbooks+. A cookbook is skipped if it appears in # +seen_cookbooks+, otherwise it is added to the set of +seen_cookbooks+ # before its dependencies are processed. def add_cookbook_with_deps(ordered_cookbooks, seen_cookbooks, cookbook) return false if seen_cookbooks.key?(cookbook) seen_cookbooks[cookbook] = true each_cookbook_dep(cookbook) do |dependency| add_cookbook_with_deps(ordered_cookbooks, seen_cookbooks, dependency) end ordered_cookbooks << cookbook end def count_files_by_segment(segment) cookbook_collection.inject(0) do |count, cookbook_by_name| count + cookbook_by_name[1].segment_filenames(segment).size end end # Lists the local paths to files in +cookbook+ of type +segment+ # (attribute, recipe, etc.), sorted lexically. def files_in_cookbook_by_segment(cookbook, segment) cookbook_collection[cookbook].segment_filenames(segment).sort end # Yields the name, as a symbol, of each cookbook depended on by # +cookbook_name+ in lexical sort order. def each_cookbook_dep(cookbook_name, &block) cookbook = cookbook_collection[cookbook_name] cookbook.metadata.dependencies.keys.sort.map{|x| x.to_sym}.each(&block) end # Given a +recipe_name+, finds the file associated with the recipe. def resolve_recipe(recipe_name) cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name) cookbook = cookbook_collection[cookbook_name] cookbook.recipe_filenames_by_name[recipe_short_name] end end end end chef-12.3.0/lib/chef/resource/0000755000004100000410000000000012520074675016044 5ustar www-datawww-datachef-12.3.0/lib/chef/resource/yum_package.rb0000644000004100000410000000326212520074675020661 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/resource/package' require 'chef/provider/package/yum' class Chef class Resource class YumPackage < Chef::Resource::Package provides :yum_package provides :package, os: "linux", platform_family: [ "rhel", "fedora" ] def initialize(name, run_context=nil) super @resource_name = :yum_package @flush_cache = { :before => false, :after => false } @allow_downgrade = false end # Install a specific arch def arch(arg=nil) set_or_return( :arch, arg, :kind_of => [ String ] ) end def flush_cache(args={}) if args.is_a? Array args.each { |arg| @flush_cache[arg] = true } elsif args.any? @flush_cache = args else @flush_cache end end def allow_downgrade(arg=nil) set_or_return( :allow_downgrade, arg, :kind_of => [ TrueClass, FalseClass ] ) end end end end chef-12.3.0/lib/chef/resource/apt_package.rb0000644000004100000410000000232212520074675020627 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/package' require 'chef/provider/package/apt' class Chef class Resource class AptPackage < Chef::Resource::Package provides :apt_package provides :package, os: "linux", platform_family: [ "debian" ] def initialize(name, run_context=nil) super @resource_name = :apt_package @default_release = nil end def default_release(arg=nil) set_or_return( :default_release, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/openbsd_package.rb0000644000004100000410000000257212520074675021504 0ustar www-datawww-data# # Authors:: AJ Christensen () # Richard Manyanza () # Scott Bonds () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2014 Richard Manyanza, Scott Bonds # 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/package' require 'chef/provider/package/openbsd' require 'chef/mixin/shell_out' class Chef class Resource class OpenbsdPackage < Chef::Resource::Package include Chef::Mixin::ShellOut provides :package, os: "openbsd" def initialize(name, run_context=nil) super @resource_name = :openbsd_package end def after_created assign_provider end private def assign_provider @provider = Chef::Provider::Package::Openbsd end end end end chef-12.3.0/lib/chef/resource/cookbook_file.rb0000644000004100000410000000300112520074675021170 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Chisamore () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2008, 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 'chef/resource/file' require 'chef/provider/cookbook_file' require 'chef/mixin/securable' class Chef class Resource class CookbookFile < Chef::Resource::File include Chef::Mixin::Securable provides :cookbook_file def initialize(name, run_context=nil) super @provider = Chef::Provider::CookbookFile @resource_name = :cookbook_file @action = "create" @source = ::File.basename(name) @cookbook = nil end def source(source_filename=nil) set_or_return(:source, source_filename, :kind_of => [ String, Array ]) end def cookbook(cookbook_name=nil) set_or_return(:cookbook, cookbook_name, :kind_of => String) end end end end chef-12.3.0/lib/chef/resource/ruby_block.rb0000644000004100000410000000254412520074675020531 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen () # 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 'chef/resource' require 'chef/provider/ruby_block' class Chef class Resource class RubyBlock < Chef::Resource identity_attr :block_name def initialize(name, run_context=nil) super @resource_name = :ruby_block @action = "run" @allowed_actions << :create << :run @block_name = name end def block(&block) if block_given? and block @block = block else @block end end def block_name(arg=nil) set_or_return( :block_name, arg, :kind_of => String ) end end end end chef-12.3.0/lib/chef/resource/pacman_package.rb0000644000004100000410000000170412520074675021305 0ustar www-datawww-data# # Author:: Jan Zimmek () # Copyright:: Copyright (c) 2010 Jan Zimmek # 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/package' class Chef class Resource class PacmanPackage < Chef::Resource::Package provides :pacman_package, os: "linux" def initialize(name, run_context=nil) super @resource_name = :pacman_package end end end end chef-12.3.0/lib/chef/resource/git.rb0000644000004100000410000000226212520074675017156 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 "chef/resource/scm" class Chef class Resource class Git < Chef::Resource::Scm provides :git def initialize(name, run_context=nil) super @resource_name = :git @additional_remotes = Hash[] end def additional_remotes(arg=nil) set_or_return( :additional_remotes, arg, :kind_of => Hash ) end alias :branch :revision alias :reference :revision alias :repo :repository end end end chef-12.3.0/lib/chef/resource/portage_package.rb0000644000004100000410000000172012520074675021505 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/package' class Chef class Resource class PortagePackage < Chef::Resource::Package def initialize(name, run_context=nil) super @resource_name = :portage_package @provider = Chef::Provider::Package::Portage end end end end chef-12.3.0/lib/chef/resource/directory.rb0000644000004100000410000000310412520074675020373 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Chisamore () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2008, 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 'chef/resource' require 'chef/provider/directory' require 'chef/mixin/securable' class Chef class Resource class Directory < Chef::Resource identity_attr :path state_attrs :group, :mode, :owner include Chef::Mixin::Securable provides :directory def initialize(name, run_context=nil) super @resource_name = :directory @path = name @action = :create @recursive = false @allowed_actions.push(:create, :delete) end def recursive(arg=nil) set_or_return( :recursive, arg, :kind_of => [ TrueClass, FalseClass ] ) end def path(arg=nil) set_or_return( :path, arg, :kind_of => String ) end end end end chef-12.3.0/lib/chef/resource/http_request.rb0000644000004100000410000000312112520074675021115 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource' require 'chef/provider/http_request' class Chef class Resource class HttpRequest < Chef::Resource identity_attr :url def initialize(name, run_context=nil) super @resource_name = :http_request @message = name @url = nil @action = :get @headers = {} @allowed_actions.push(:get, :put, :post, :delete, :head, :options) end def url(args=nil) set_or_return( :url, args, :kind_of => String ) end def message(args=nil, &block) args = block if block_given? set_or_return( :message, args, :kind_of => Object ) end def headers(args=nil) set_or_return( :headers, args, :kind_of => Hash ) end end end end chef-12.3.0/lib/chef/resource/breakpoint.rb0000644000004100000410000000175412520074675020536 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/resource' class Chef class Resource class Breakpoint < Chef::Resource def initialize(action="break", *args) @name = caller.first super(@name, *args) @action = "break" @allowed_actions << :break @resource_name = :breakpoint end end end end chef-12.3.0/lib/chef/resource/remote_directory.rb0000644000004100000410000000565212520074675021760 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource/directory' require 'chef/provider/remote_directory' require 'chef/mixin/securable' class Chef class Resource class RemoteDirectory < Chef::Resource::Directory include Chef::Mixin::Securable provides :remote_directory identity_attr :path state_attrs :files_owner, :files_group, :files_mode def initialize(name, run_context=nil) super @resource_name = :remote_directory @path = name @source = ::File.basename(name) @delete = false @action = :create @recursive = true @purge = false @files_backup = 5 @files_owner = nil @files_group = nil @files_mode = 0644 unless Chef::Platform.windows? @overwrite = true @allowed_actions.push(:create, :create_if_missing, :delete) @cookbook = nil end if Chef::Platform.windows? # create a second instance of the 'rights' attribute rights_attribute(:files_rights) end def source(args=nil) set_or_return( :source, args, :kind_of => String ) end def files_backup(arg=nil) set_or_return( :files_backup, arg, :kind_of => [ Integer, FalseClass ] ) end def purge(arg=nil) set_or_return( :purge, arg, :kind_of => [ TrueClass, FalseClass ] ) end def files_group(arg=nil) set_or_return( :files_group, arg, :regex => Chef::Config[:group_valid_regex] ) end def files_mode(arg=nil) set_or_return( :files_mode, arg, :regex => /^\d{3,4}$/ ) end def files_owner(arg=nil) set_or_return( :files_owner, arg, :regex => Chef::Config[:user_valid_regex] ) end def overwrite(arg=nil) set_or_return( :overwrite, arg, :kind_of => [ TrueClass, FalseClass ] ) end def cookbook(args=nil) set_or_return( :cookbook, args, :kind_of => String ) end end end end chef-12.3.0/lib/chef/resource/scm.rb0000644000004100000410000000757012520074675017164 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/resource' class Chef class Resource class Scm < Chef::Resource identity_attr :destination state_attrs :revision def initialize(name, run_context=nil) super @destination = name @resource_name = :scm @enable_submodules = false @enable_checkout = true @revision = "HEAD" @remote = "origin" @ssh_wrapper = nil @depth = nil @allowed_actions.push(:checkout, :export, :sync, :diff, :log) @action = [:sync] @checkout_branch = "deploy" @environment = nil end def destination(arg=nil) set_or_return( :destination, arg, :kind_of => String ) end def repository(arg=nil) set_or_return( :repository, arg, :kind_of => String ) end def revision(arg=nil) set_or_return( :revision, arg, :kind_of => String ) end def user(arg=nil) set_or_return( :user, arg, :kind_of => [String, Integer] ) end def group(arg=nil) set_or_return( :group, arg, :kind_of => [String, Integer] ) end def svn_username(arg=nil) set_or_return( :svn_username, arg, :kind_of => String ) end def svn_password(arg=nil) set_or_return( :svn_password, arg, :kind_of => String ) end def svn_arguments(arg=nil) @svn_arguments, arg = nil, nil if arg == false set_or_return( :svn_arguments, arg, :kind_of => String ) end def svn_info_args(arg=nil) @svn_info_args, arg = nil, nil if arg == false set_or_return( :svn_info_args, arg, :kind_of => String) end # Capistrano and git-deploy use ``shallow clone'' def depth(arg=nil) set_or_return( :depth, arg, :kind_of => Integer ) end def enable_submodules(arg=nil) set_or_return( :enable_submodules, arg, :kind_of => [TrueClass, FalseClass] ) end def enable_checkout(arg=nil) set_or_return( :enable_checkout, arg, :kind_of => [TrueClass, FalseClass] ) end def remote(arg=nil) set_or_return( :remote, arg, :kind_of => String ) end def ssh_wrapper(arg=nil) set_or_return( :ssh_wrapper, arg, :kind_of => String ) end def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => Integer ) end def checkout_branch(arg=nil) set_or_return( :checkout_branch, arg, :kind_of => String ) end def environment(arg=nil) set_or_return( :environment, arg, :kind_of => [ Hash ] ) end alias :env :environment end end end chef-12.3.0/lib/chef/resource/easy_install_package.rb0000644000004100000410000000263712520074675022543 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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/package' class Chef class Resource class EasyInstallPackage < Chef::Resource::Package provides :easy_install_package def initialize(name, run_context=nil) super @resource_name = :easy_install_package end def easy_install_binary(arg=nil) set_or_return( :easy_install_binary, arg, :kind_of => [ String ] ) end def python_binary(arg=nil) set_or_return( :python_install_binary, arg, :kind_of => [ String ] ) end def module_name(arg=nil) set_or_return( :module_name, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/execute.rb0000644000004100000410000001017312520074675020035 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource' require 'chef/provider/execute' class Chef class Resource class Execute < Chef::Resource identity_attr :command # The ResourceGuardInterpreter wraps a resource's guards in another resource. That inner resource # needs to behave differently during (for example) why_run mode, so we flag it here. For why_run mode # we still want to execute the guard resource even if we are not executing the wrapping resource. # Only execute resources (and subclasses) can be guard interpreters. attr_accessor :is_guard_interpreter def initialize(name, run_context=nil) super @resource_name = :execute @command = name @backup = 5 @action = "run" @creates = nil @cwd = nil @environment = nil @group = nil @path = nil @returns = 0 @timeout = nil @user = nil @allowed_actions.push(:run) @umask = nil @default_guard_interpreter = :execute @is_guard_interpreter = false end def umask(arg=nil) set_or_return( :umask, arg, :kind_of => [ String, Integer ] ) end def command(arg=nil) set_or_return( :command, arg, :kind_of => [ String, Array ] ) end def creates(arg=nil) set_or_return( :creates, arg, :kind_of => [ String ] ) end def cwd(arg=nil) set_or_return( :cwd, arg, :kind_of => [ String ] ) end def environment(arg=nil) set_or_return( :environment, arg, :kind_of => [ Hash ] ) end alias :env :environment def group(arg=nil) set_or_return( :group, arg, :kind_of => [ String, Integer ] ) end def path(arg=nil) Chef::Log.warn "'path' attribute of 'execute' is not used by any provider in Chef 11 and Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13." set_or_return( :path, arg, :kind_of => [ Array ] ) end def returns(arg=nil) set_or_return( :returns, arg, :kind_of => [ Integer, Array ] ) end def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => [ Integer, Float ] ) end def user(arg=nil) set_or_return( :user, arg, :kind_of => [ String, Integer ] ) end def self.set_guard_inherited_attributes(*inherited_attributes) @class_inherited_attributes = inherited_attributes end def self.guard_inherited_attributes(*inherited_attributes) # Similar to patterns elsewhere, return attributes from this # class and superclasses as a form of inheritance ancestor_attributes = [] if superclass.respond_to?(:guard_inherited_attributes) ancestor_attributes = superclass.guard_inherited_attributes end ancestor_attributes.concat(@class_inherited_attributes ? @class_inherited_attributes : []).uniq end set_guard_inherited_attributes( :cwd, :environment, :group, :user, :umask ) end end end chef-12.3.0/lib/chef/resource/ifconfig.rb0000644000004100000410000000564012520074675020162 0ustar www-datawww-data# # Author:: Jason K. Jackson (jasonjackson@gmail.com) # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Jason K. Jackson # 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' class Chef class Resource class Ifconfig < Chef::Resource identity_attr :device state_attrs :inet_addr, :mask def initialize(name, run_context=nil) super @resource_name = :ifconfig @target = name @action = :add @allowed_actions.push(:add, :delete, :enable, :disable) @hwaddr = nil @mask = nil @inet_addr = nil @bcast = nil @mtu = nil @metric = nil @device = nil @onboot = nil @network = nil @bootproto = nil @onparent = nil end def target(arg=nil) set_or_return( :target, arg, :kind_of => String ) end def device(arg=nil) set_or_return( :device, arg, :kind_of => String ) end def hwaddr(arg=nil) set_or_return( :hwaddr, arg, :kind_of => String ) end def inet_addr(arg=nil) set_or_return( :inet_addr, arg, :kind_of => String ) end def bcast(arg=nil) set_or_return( :bcast, arg, :kind_of => String ) end def mask(arg=nil) set_or_return( :mask, arg, :kind_of => String ) end def mtu(arg=nil) set_or_return( :mtu, arg, :kind_of => String ) end def metric(arg=nil) set_or_return( :metric, arg, :kind_of => String ) end def onboot(arg=nil) set_or_return( :onboot, arg, :kind_of => String ) end def network(arg=nil) set_or_return( :network, arg, :kind_of => String ) end def bootproto(arg=nil) set_or_return( :bootproto, arg, :kind_of => String ) end def onparent(arg=nil) set_or_return( :onparent, arg, :kind_of => String ) end end end end chef-12.3.0/lib/chef/resource/mount.rb0000644000004100000410000000777712520074675017555 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Tyler Cloke () # 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/resource' class Chef class Resource class Mount < Chef::Resource identity_attr :device state_attrs :mount_point, :device_type, :fstype, :username, :password, :domain provides :mount def initialize(name, run_context=nil) super @resource_name = :mount @mount_point = name @device = nil @device_type = :device @fsck_device = '-' @fstype = "auto" @options = ["defaults"] @dump = 0 @pass = 2 @mounted = false @enabled = false @action = :mount @supports = { :remount => false } @allowed_actions.push(:mount, :umount, :remount, :enable, :disable) @username = nil @password = nil @domain = nil end def mount_point(arg=nil) set_or_return( :mount_point, arg, :kind_of => [ String ] ) end def device(arg=nil) set_or_return( :device, arg, :kind_of => [ String ] ) end def device_type(arg=nil) real_arg = arg.kind_of?(String) ? arg.to_sym : arg valid_devices = if RUBY_PLATFORM =~ /solaris/i [ :device ] else [ :device, :label, :uuid ] end set_or_return( :device_type, real_arg, :equal_to => valid_devices ) end def fsck_device(arg=nil) set_or_return( :fsck_device, arg, :kind_of => [ String ] ) end def fstype(arg=nil) set_or_return( :fstype, arg, :kind_of => [ String ] ) end def options(arg=nil) ret = set_or_return( :options, arg, :kind_of => [ Array, String ] ) if ret.is_a? String ret.gsub(/,/, ' ').split(/ /) else ret end end def dump(arg=nil) set_or_return( :dump, arg, :kind_of => [ Integer, FalseClass ] ) end def pass(arg=nil) set_or_return( :pass, arg, :kind_of => [ Integer, FalseClass ] ) end def mounted(arg=nil) set_or_return( :mounted, arg, :kind_of => [ TrueClass, FalseClass ] ) end def enabled(arg=nil) set_or_return( :enabled, arg, :kind_of => [ TrueClass, FalseClass ] ) end def supports(args={}) if args.is_a? Array args.each { |arg| @supports[arg] = true } elsif args.any? @supports = args else @supports end end def username(arg=nil) set_or_return( :username, arg, :kind_of => [ String ] ) end def password(arg=nil) set_or_return( :password, arg, :kind_of => [ String ] ) end def domain(arg=nil) set_or_return( :domain, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/file/0000755000004100000410000000000012520074675016763 5ustar www-datawww-datachef-12.3.0/lib/chef/resource/file/verification.rb0000644000004100000410000000763712520074675022007 0ustar www-datawww-data# # Author:: Steven Danna () # Copyright:: Copyright (c) 2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/exceptions' require 'chef/guard_interpreter' require 'chef/mixin/descendants_tracker' class Chef class Resource class File < Chef::Resource # # See RFC 027 for a full specification # # File verifications allow user-supplied commands a means of # preventing file reosurce content deploys. Their intended use # is to verify the contents of a temporary file before it is # deployed onto the system. # # Similar to not_if and only_if, file verifications can take a # ruby block, which will be called, or a string, which will be # executed as a Shell command. # # Additonally, Chef or third-party verifications can ship # "registered verifications" that the user can use by specifying # a :symbol as the command name. # # To create a registered verification, create a class that # inherits from Chef::Resource::File::Verification and use the # provides class method to give it name. Registered # verifications are expected to supply a verify instance method # that takes 2 arguments. # # Example: # class Chef # class Resource # class File::Verification::Foo < Chef::Resource::File::Verification # provides :noop # def verify(path, opts) # #yolo # true # end # end # end # end # # class Verification extend Chef::Mixin::DescendantsTracker def self.provides(name) @provides = name end def self.provides?(name) @provides == name end def self.lookup(name) c = descendants.find {|d| d.provides?(name) } if c.nil? raise Chef::Exceptions::VerificationNotFound.new "No file verification for #{name} found." end c end def initialize(parent_resource, command, opts, &block) @command, @command_opts = command, opts @block = block @parent_resource = parent_resource end def verify(path, opts={}) Chef::Log.debug("Running verification[#{self}] on #{path}") if @block verify_block(path, opts) elsif @command.is_a?(Symbol) verify_registered_verification(path, opts) elsif @command.is_a?(String) verify_command(path, opts) end end # opts is currently unused, but included in the API # to support future extensions def verify_block(path, opts) @block.call(path) end # We reuse Chef::GuardInterpreter in order to support # the same set of options that the not_if/only_if blocks do def verify_command(path, opts) command = @command % {:file => path} interpreter = Chef::GuardInterpreter.for_resource(@parent_resource, command, @command_opts) interpreter.evaluate end def verify_registered_verification(path, opts) verification_class = Chef::Resource::File::Verification.lookup(@command) v = verification_class.new(@parent_resource, @command, @command_opts, &@block) v.verify(path, opts) end end end end end chef-12.3.0/lib/chef/resource/dsc_script.rb0000644000004100000410000000674012520074675020535 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/exceptions' class Chef class Resource class DscScript < Chef::Resource provides :dsc_script, platform: "windows" def initialize(name, run_context=nil) super @allowed_actions.push(:run) @action = :run @resource_name = :dsc_script @imports = {} end def code(arg=nil) if arg && command raise ArgumentError, "Only one of 'code' and 'command' attributes may be specified" end if arg && configuration_name raise ArgumentError, "The 'code' and 'command' attributes may not be used together" end set_or_return( :code, arg, :kind_of => [ String ] ) end def configuration_name(arg=nil) if arg && code raise ArgumentError, "Attribute `configuration_name` may not be set if `code` is set" end set_or_return( :configuration_name, arg, :kind_of => [ String ] ) end def command(arg=nil) if arg && code raise ArgumentError, "The 'code' and 'command' attributes may not be used together" end set_or_return( :command, arg, :kind_of => [ String ] ) end def configuration_data(arg=nil) if arg && configuration_data_script raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together" end set_or_return( :configuration_data, arg, :kind_of => [ String ] ) end def configuration_data_script(arg=nil) if arg && configuration_data raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together" end set_or_return( :configuration_data_script, arg, :kind_of => [ String ] ) end def imports(module_name=nil, *args) if module_name @imports[module_name] ||= [] if args.length == 0 @imports[module_name] << '*' else @imports[module_name].push(*args) end else @imports end end def flags(arg=nil) set_or_return( :flags, arg, :kind_of => [ Hash ] ) end def cwd(arg=nil) set_or_return( :cwd, arg, :kind_of => [ String ] ) end def environment(arg=nil) set_or_return( :environment, arg, :kind_of => [ Hash ] ) end def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => [ Integer ] ) end end end end chef-12.3.0/lib/chef/resource/chef_gem.rb0000644000004100000410000000457012520074675020134 0ustar www-datawww-data# # Author:: Bryan McLellan # 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/resource/package' require 'chef/resource/gem_package' class Chef class Resource class ChefGem < Chef::Resource::Package::GemPackage provides :chef_gem def initialize(name, run_context=nil) super @resource_name = :chef_gem @compile_time = Chef::Config[:chef_gem_compile_time] @gem_binary = RbConfig::CONFIG['bindir'] + "/gem" end # The chef_gem resources is for installing gems to the current gem environment only for use by Chef cookbooks. def gem_binary(arg=nil) if arg raise ArgumentError, "The chef_gem resource is restricted to the current gem environment, use gem_package to install to other environments." end @gem_binary end def compile_time(arg=nil) set_or_return( :compile_time, arg, :kind_of => [ TrueClass, FalseClass ] ) end def after_created # Chef::Resource.run_action: Caveat: this skips Chef::Runner.run_action, where notifications are handled # Action could be an array of symbols, but probably won't (think install + enable for a package) if compile_time.nil? Chef::Log.deprecation "#{self} chef_gem compile_time installation is deprecated" Chef::Log.deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior." Chef::Log.deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required." end if compile_time || compile_time.nil? Array(action).each do |action| self.run_action(action) end Gem.clear_paths end end end end end chef-12.3.0/lib/chef/resource/erl_call.rb0000644000004100000410000000414612520074675020153 0ustar www-datawww-data# # Author:: Joe Williams () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Joe Williams # 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' require 'chef/provider/erl_call' class Chef class Resource class ErlCall < Chef::Resource # erl_call : http://erlang.org/doc/man/erl_call.html identity_attr :code def initialize(name, run_context=nil) super @resource_name = :erl_call @code = "q()." # your erlang code goes here @cookie = nil # cookie of the erlang node @distributed = false # if you want to have a distributed erlang node @name_type = "sname" # type of erlang hostname name or sname @node_name = "chef@localhost" # the erlang node hostname @action = "run" @allowed_actions.push(:run) end def code(arg=nil) set_or_return( :code, arg, :kind_of => [ String ] ) end def cookie(arg=nil) set_or_return( :cookie, arg, :kind_of => [ String ] ) end def distributed(arg=nil) set_or_return( :distributed, arg, :kind_of => [ TrueClass, FalseClass ] ) end def name_type(arg=nil) set_or_return( :name_type, arg, :kind_of => [ String ] ) end def node_name(arg=nil) set_or_return( :node_name, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/ruby.rb0000644000004100000410000000170112520074675017351 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/script' require 'chef/provider/script' class Chef class Resource class Ruby < Chef::Resource::Script def initialize(name, run_context=nil) super @resource_name = :ruby @interpreter = "ruby" end end end end chef-12.3.0/lib/chef/resource/deploy_revision.rb0000644000004100000410000000230712520074675021605 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource # Convenience class for using the deploy resource with the revision # deployment strategy (provider) class DeployRevision < Chef::Resource::Deploy provides :deploy_revision def initialize(*args, &block) super @resource_name = :deploy_revision end end class DeployBranch < Chef::Resource::DeployRevision provides :deploy_branch def initialize(*args, &block) super @resource_name = :deploy_branch end end end end chef-12.3.0/lib/chef/resource/ips_package.rb0000644000004100000410000000237612520074675020647 0ustar www-datawww-data# # Author:: Jason Williams () # 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 'chef/resource/package' require 'chef/provider/package/ips' class Chef class Resource class IpsPackage < ::Chef::Resource::Package provides :ips_package, os: "solaris2" def initialize(name, run_context = nil) super(name, run_context) @resource_name = :ips_package @allowed_actions.push(:install, :remove, :upgrade) @accept_license = false end def accept_license(arg=nil) set_or_return( :purge, arg, :kind_of => [ TrueClass, FalseClass ] ) end end end end chef-12.3.0/lib/chef/resource/resource_notification.rb0000644000004100000410000001200512520074675022764 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource' class Chef class Resource class Notification < Struct.new(:resource, :action, :notifying_resource) def duplicates?(other_notification) unless other_notification.respond_to?(:resource) && other_notification.respond_to?(:action) msg = "only duck-types of Chef::Resource::Notification can be checked for duplication "\ "you gave #{other_notification.inspect}" raise ArgumentError, msg end other_notification.resource == resource && other_notification.action == action end # If resource and/or notifying_resource is not a resource object, this will look them up in the resource collection # and fix the references from strings to actual Resource objects. def resolve_resource_reference(resource_collection) return resource if resource.kind_of?(Chef::Resource) && notifying_resource.kind_of?(Chef::Resource) if not(resource.kind_of?(Chef::Resource)) fix_resource_reference(resource_collection) end if not(notifying_resource.kind_of?(Chef::Resource)) fix_notifier_reference(resource_collection) end end # This will look up the resource if it is not a Resource Object. It will complain if it finds multiple # resources, can't find a resource, or gets invalid syntax. def fix_resource_reference(resource_collection) matching_resource = resource_collection.find(resource) if Array(matching_resource).size > 1 msg = "Notification #{self} from #{notifying_resource} was created with a reference to multiple resources, "\ "but can only notify one resource. Notifying resource was defined on #{notifying_resource.source_line}" raise Chef::Exceptions::InvalidResourceReference, msg end self.resource = matching_resource rescue Chef::Exceptions::ResourceNotFound => e err = Chef::Exceptions::ResourceNotFound.new(<<-FAIL) resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \ but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \ #{notifying_resource.source_line} FAIL err.set_backtrace(e.backtrace) raise err rescue Chef::Exceptions::InvalidResourceSpecification => e err = Chef::Exceptions::InvalidResourceSpecification.new(<<-F) Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \ but #{resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \ is defined near #{notifying_resource.source_line} F err.set_backtrace(e.backtrace) raise err end # This will look up the notifying_resource if it is not a Resource Object. It will complain if it finds multiple # resources, can't find a resource, or gets invalid syntax. def fix_notifier_reference(resource_collection) matching_notifier = resource_collection.find(notifying_resource) if Array(matching_notifier).size > 1 msg = "Notification #{self} from #{notifying_resource} was created with a reference to multiple notifying "\ "resources, but can only originate from one resource. Destination resource was defined "\ "on #{resource.source_line}" raise Chef::Exceptions::InvalidResourceReference, msg end self.notifying_resource = matching_notifier rescue Chef::Exceptions::ResourceNotFound => e err = Chef::Exceptions::ResourceNotFound.new(<<-FAIL) Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \ but #{notifying_resource} cannot be found in the resource collection. #{resource} is defined in \ #{resource.source_line} FAIL err.set_backtrace(e.backtrace) raise err rescue Chef::Exceptions::InvalidResourceSpecification => e err = Chef::Exceptions::InvalidResourceSpecification.new(<<-F) Resource #{resource} is configured to receive notifications from #{notifying_resource} with action #{action}, \ but #{notifying_resource.inspect} is not valid syntax to look up a resource in the resource collection. Notification \ is defined near #{resource.source_line} F err.set_backtrace(e.backtrace) raise err end end end end chef-12.3.0/lib/chef/resource/solaris_package.rb0000644000004100000410000000244612520074675021526 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Author:: Prabhu Das () # 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/package' require 'chef/provider/package/solaris' class Chef class Resource class SolarisPackage < Chef::Resource::Package provides :solaris_package provides :package, os: "solaris2", platform_family: "nexentacore" provides :package, os: "solaris2", platform_family: "solaris2" do |node| # on >= Solaris 11 we default to IPS packages instead node[:platform_version].to_f <= 5.10 end def initialize(name, run_context=nil) super @resource_name = :solaris_package end end end end chef-12.3.0/lib/chef/resource/conditional.rb0000644000004100000410000001007612520074675020700 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/mixin/shell_out' require 'chef/guard_interpreter' class Chef class Resource class Conditional include Chef::Mixin::ShellOut # We only create these via the `not_if` or `only_if` constructors, and # not the default constructor class << self private :new end def self.not_if(parent_resource, command=nil, command_opts={}, &block) new(:not_if, parent_resource, command, command_opts, &block) end def self.only_if(parent_resource, command=nil, command_opts={}, &block) new(:only_if, parent_resource, command, command_opts, &block) end attr_reader :positivity attr_reader :command attr_reader :command_opts attr_reader :block def initialize(positivity, parent_resource, command=nil, command_opts={}, &block) @positivity = positivity @command, @command_opts = command, command_opts @block = block @block_given = block_given? @parent_resource = parent_resource raise ArgumentError, "only_if/not_if requires either a command or a block" unless command || block_given? end def configure case @command when String,Array @guard_interpreter = Chef::GuardInterpreter.for_resource(@parent_resource, @command, @command_opts) @block = nil when nil # We should have a block if we get here # Check to see if the user set the guard_interpreter on the parent resource. Note that # this error will not be raised when using the default_guard_interpreter if @parent_resource.guard_interpreter != @parent_resource.default_guard_interpreter msg = "#{@parent_resource.name} was given a guard_interpreter of #{@parent_resource.guard_interpreter}, " msg << "but not given a command as a string. guard_interpreter does not support blocks (because they just contain ruby)." raise ArgumentError, msg end @guard_interpreter = nil @command, @command_opts = nil, nil else # command was passed, but it wasn't a String raise ArgumentError, "Invalid only_if/not_if command, expected a string: #{command.inspect} (#{command.class})" end end # this is run during convergence via Chef::Resource#run_action -> Chef::Resource#should_skip? def continue? # configure late in case guard_interpreter is specified on the resource after the conditional configure case @positivity when :only_if evaluate when :not_if !evaluate else raise "Cannot evaluate resource conditional of type #{@positivity}" end end def evaluate @guard_interpreter ? evaluate_command : evaluate_block end def evaluate_command @guard_interpreter.evaluate rescue Chef::Exceptions::CommandTimeout Chef::Log.warn "Command '#{@command}' timed out" false end def evaluate_block @block.call end def short_description @positivity end def description cmd_or_block = @command ? "command `#{@command}`" : "ruby block" "#{@positivity} #{cmd_or_block}" end def to_text if @command "#{positivity} \"#{@command}\"" else "#{@positivity} { #code block }" end end end end end chef-12.3.0/lib/chef/resource/freebsd_package.rb0000644000004100000410000000405612520074675021463 0ustar www-datawww-data# # Authors:: AJ Christensen () # Richard Manyanza () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2014 Richard Manyanza. # 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/package' require 'chef/provider/package/freebsd/port' require 'chef/provider/package/freebsd/pkg' require 'chef/provider/package/freebsd/pkgng' require 'chef/mixin/shell_out' class Chef class Resource class FreebsdPackage < Chef::Resource::Package include Chef::Mixin::ShellOut provides :package, platform: "freebsd" def initialize(name, run_context=nil) super @resource_name = :freebsd_package end def after_created assign_provider end def supports_pkgng? ships_with_pkgng? || !!shell_out!("make -V WITH_PKGNG", :env => nil).stdout.match(/yes/i) end private def ships_with_pkgng? # It was not until __FreeBSD_version 1000017 that pkgng became # the default binary package manager. See '/usr/ports/Mk/bsd.port.mk'. node.automatic[:os_version].to_i >= 1000017 end def assign_provider @provider = if @source.to_s =~ /^ports$/i Chef::Provider::Package::Freebsd::Port elsif supports_pkgng? Chef::Provider::Package::Freebsd::Pkgng else Chef::Provider::Package::Freebsd::Pkg end end end end end chef-12.3.0/lib/chef/resource/whyrun_safe_ruby_block.rb0000644000004100000410000000160212520074675023135 0ustar www-datawww-data# # Author:: Phil Dibowitz () # Copyright:: Copyright (c) 2013 Facebook # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource class WhyrunSafeRubyBlock < Chef::Resource::RubyBlock def initialize(name, run_context=nil) super @resource_name = :whyrun_safe_ruby_block end end end end chef-12.3.0/lib/chef/resource/macports_package.rb0000644000004100000410000000171212520074675021675 0ustar www-datawww-data# # Author:: David Balatero () # 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. # class Chef class Resource class MacportsPackage < Chef::Resource::Package provides :macports_package provides :package, os: "darwin" def initialize(name, run_context=nil) super @resource_name = :macports_package end end end end chef-12.3.0/lib/chef/resource/gem_package.rb0000644000004100000410000000353112520074675020616 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/package' class Chef class Resource class GemPackage < Chef::Resource::Package provides :gem_package def initialize(name, run_context=nil) super @resource_name = :gem_package @clear_sources = false end def source(arg=nil) set_or_return(:source, arg, :kind_of => [ String, Array ]) end def clear_sources(arg=nil) set_or_return(:clear_sources, arg, :kind_of => [ TrueClass, FalseClass ]) end # Sets a custom gem_binary to run for gem commands. def gem_binary(gem_cmd=nil) set_or_return(:gem_binary,gem_cmd,:kind_of => [ String ]) end ## # Options for the gem install, either a Hash or a String. When a hash is # given, the options are passed to Gem::DependencyInstaller.new, and the # gem will be installed via the gems API. When a String is given, the gem # will be installed by shelling out to the gem command. Using a Hash of # options with an explicit gem_binary will result in undefined behavior. def options(opts=nil) set_or_return(:options,opts,:kind_of => [String,Hash]) end end end end chef-12.3.0/lib/chef/resource/lwrp_base.rb0000644000004100000410000001263412520074675020355 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008-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/resource' class Chef class Resource # == Chef::Resource::LWRPBase # Base class for LWRP resources. Adds DSL sugar on top of Chef::Resource, # so attributes, default action, etc. can be defined with pleasing syntax. class LWRPBase < Resource NULL_ARG = Object.new extend Chef::Mixin::ConvertToClassName extend Chef::Mixin::FromFile # Evaluates the LWRP resource file and instantiates a new Resource class. def self.build_from_file(cookbook_name, filename, run_context) resource_class = nil rname = filename_to_qualified_string(cookbook_name, filename) class_name = convert_to_class_name(rname) if Resource.const_defined?(class_name, false) Chef::Log.info("#{class_name} light-weight resource is already initialized -- Skipping loading #{filename}!") Chef::Log.debug("Overriding already defined LWRPs is not supported anymore starting with Chef 12.") resource_class = Resource.const_get(class_name) else resource_class = Class.new(self) Chef::Resource.const_set(class_name, resource_class) resource_class.resource_name = rname resource_class.run_context = run_context resource_class.class_from_file(filename) Chef::Log.debug("Loaded contents of #{filename} into a resource named #{rname} defined in Chef::Resource::#{class_name}") end resource_class end # Set the resource name for this LWRP def self.resource_name(arg = NULL_ARG) if arg.equal?(NULL_ARG) @resource_name else @resource_name = arg end end class << self alias_method :resource_name=, :resource_name end # Define an attribute on this resource, including optional validation # parameters. def self.attribute(attr_name, validation_opts={}) define_method(attr_name) do |arg=nil| set_or_return(attr_name.to_sym, arg, validation_opts) end end # Sets the default action def self.default_action(action_name=NULL_ARG) unless action_name.equal?(NULL_ARG) @actions ||= [] if action_name.is_a?(Array) action = action_name.map { |arg| arg.to_sym } @actions = actions | action @default_action = action else action = action_name.to_sym @actions.push(action) unless @actions.include?(action) @default_action = action end end @default_action ||= from_superclass(:default_action) end # Adds +action_names+ to the list of valid actions for this resource. def self.actions(*action_names) if action_names.empty? defined?(@actions) ? @actions : from_superclass(:actions, []).dup else # BC-compat way for checking if actions have already been defined if defined?(@actions) @actions.push(*action_names) else @actions = action_names end end end # @deprecated def self.valid_actions(*args) Chef::Log.warn("`valid_actions' is deprecated, please use actions `instead'!") actions(*args) end # Set the run context on the class. Used to provide access to the node # during class definition. def self.run_context=(run_context) @run_context = run_context end def self.run_context @run_context end def self.node run_context.node end def self.lazy(&block) DelayedEvaluator.new(&block) end private # Get the value from the superclass, if it responds, otherwise return # +nil+. Since class instance variables are **not** inherited upon # subclassing, this is a required check to ensure Chef pulls the # +default_action+ and other DSL-y methods when extending LWRP::Base. def self.from_superclass(m, default = nil) return default if superclass == Chef::Resource::LWRPBase superclass.respond_to?(m) ? superclass.send(m) : default end # Default initializer. Sets the default action and allowed actions. def initialize(name, run_context=nil) super(name, run_context) # Raise an exception if the resource_name was not defined if self.class.resource_name.nil? raise Chef::Exceptions::InvalidResourceSpecification, "You must specify `resource_name'!" end @resource_name = self.class.resource_name.to_sym @action = self.class.default_action allowed_actions.push(self.class.actions).flatten! end end end end chef-12.3.0/lib/chef/resource/python.rb0000644000004100000410000000170512520074675017715 0ustar www-datawww-data# Author:: Adam Jacob () # 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 'chef/resource/script' require 'chef/provider/script' class Chef class Resource class Python < Chef::Resource::Script def initialize(name, run_context=nil) super @resource_name = :python @interpreter = "python" end end end end chef-12.3.0/lib/chef/resource/ohai.rb0000644000004100000410000000250712520074675017315 0ustar www-datawww-data# # Author:: Michael Leinartas () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2010 Michael Leinartas # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource class Ohai < Chef::Resource identity_attr :name state_attrs :plugin def initialize(name, run_context=nil) super @resource_name = :ohai @name = name @allowed_actions.push(:reload) @action = :reload @plugin = nil end def plugin(arg=nil) set_or_return( :plugin, arg, :kind_of => [ String ] ) end def name(arg=nil) set_or_return( :name, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/dsc_resource.rb0000644000004100000410000000405412520074675021054 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/dsl/powershell' class Chef class Resource class DscResource < Chef::Resource provides :dsc_resource, os: "windows" include Chef::DSL::Powershell def initialize(name, run_context) super @properties = {} @resource_name = :dsc_resource @resource = nil @allowed_actions.push(:run) @action = :run end def resource(value=nil) if value @resource = value else @resource end end def module_name(value=nil) if value @module_name = value else @module_name end end def property(property_name, value=nil) if not property_name.is_a?(Symbol) raise TypeError, "A property name of type Symbol must be specified, '#{property_name.to_s}' of type #{property_name.class.to_s} was given" end if value.nil? value_of(@properties[property_name]) else @properties[property_name] = value end end def properties @properties.reduce({}) do |memo, (k, v)| memo[k] = value_of(v) memo end end private def value_of(value) if value.is_a?(DelayedEvaluator) value.call else value end end end end end chef-12.3.0/lib/chef/resource/bff_package.rb0000644000004100000410000000170712520074675020606 0ustar www-datawww-data# # Author:: Deepali Jagtap () # 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/package' require 'chef/provider/package/aix' class Chef class Resource class BffPackage < Chef::Resource::Package def initialize(name, run_context=nil) super @resource_name = :bff_package end end end end chef-12.3.0/lib/chef/resource/registry_key.rb0000644000004100000410000001160212520074675021111 0ustar www-datawww-data# Author:: Prajakta Purohit () # Author:: Lamont Granquist () # # Copyright:: 2011, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/provider/registry_key' require 'chef/resource' require 'chef/digester' class Chef class Resource class RegistryKey < Chef::Resource identity_attr :key state_attrs :values # Some registry key data types may not be safely reported as json. # Example (CHEF-5323): # # registry_key 'HKEY_CURRENT_USER\\ChefTest2014' do # values [{ # :name => "ValueWithBadData", # :type => :binary, # :data => 255.chr * 1 # }] # action :create # end # # will raise Encoding::UndefinedConversionError: "\xFF" from ASCII-8BIT to UTF-8. # # To avoid sending data that cannot be nicely converted for json, we have # the values method return "safe" data if the data type is "unsafe". Known "unsafe" # data types are :binary, :dword, :dword-big-endian, and :qword. If other # criteria generate data that cannot reliably be sent as json, add that criteria # to the needs_checksum? method. When unsafe data is detected, the values method # returns an md5 checksum of the listed data. # # :unscrubbed_values returns the values exactly as provided in the resource (i.e., # data is not checksummed, regardless of the data type/"unsafe" criteria). # # Future: # If we have conflicts with other resources reporting json incompatible state, we # may want to extend the state_attrs API with the ability to rename POST'd attrs. # # See lib/chef/resource_reporter.rb for more information. attr_reader :unscrubbed_values def initialize(name, run_context=nil) super @resource_name = :registry_key @action = :create @architecture = :machine @recursive = false @key = name @values, @unscrubbed_values = [], [] @allowed_actions.push(:create, :create_if_missing, :delete, :delete_key) end def key(arg=nil) set_or_return( :key, arg, :kind_of => String ) end def values(arg=nil) if not arg.nil? if arg.is_a?(Hash) @values = [ arg ] elsif arg.is_a?(Array) @values = arg else raise ArgumentError, "Bad type for RegistryKey resource, use Hash or Array" end @values.each do |v| raise ArgumentError, "Missing name key in RegistryKey values hash" unless v.has_key?(:name) raise ArgumentError, "Missing type key in RegistryKey values hash" unless v.has_key?(:type) raise ArgumentError, "Missing data key in RegistryKey values hash" unless v.has_key?(:data) v.each_key do |key| raise ArgumentError, "Bad key #{key} in RegistryKey values hash" unless [:name,:type,:data].include?(key) end raise ArgumentError, "Type of name => #{v[:name]} should be string" unless v[:name].is_a?(String) raise Argument Error "Type of type => #{v[:name]} should be symbol" unless v[:type].is_a?(Symbol) end @unscrubbed_values = @values elsif self.instance_variable_defined?(:@values) scrub_values(@values) end end def recursive(arg=nil) set_or_return( :recursive, arg, :kind_of => [TrueClass, FalseClass] ) end def architecture(arg=nil) set_or_return( :architecture, arg, :kind_of => Symbol ) end private def scrub_values(values) scrubbed = [] values.each do |value| scrubbed_value = value.dup if needs_checksum?(scrubbed_value) data_io = StringIO.new(scrubbed_value[:data].to_s) scrubbed_value[:data] = Chef::Digester.instance.generate_md5_checksum(data_io) end scrubbed << scrubbed_value end scrubbed end # Some data types may raise errors when sent as json. Returns true if this # value's data may need to be converted to a checksum. def needs_checksum?(value) unsafe_types = [:binary, :dword, :dword_big_endian, :qword] unsafe_types.include?(value[:type]) end end end end chef-12.3.0/lib/chef/resource/link.rb0000644000004100000410000000524512520074675017334 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource' require 'chef/mixin/securable' class Chef class Resource class Link < Chef::Resource include Chef::Mixin::Securable provides :link identity_attr :target_file state_attrs :to, :owner, :group def initialize(name, run_context=nil) verify_links_supported! super @resource_name = :link @to = nil @action = :create @link_type = :symbolic @target_file = name @allowed_actions.push(:create, :delete) end def to(arg=nil) set_or_return( :to, arg, :kind_of => String ) end def target_file(arg=nil) set_or_return( :target_file, arg, :kind_of => String ) end def link_type(arg=nil) real_arg = arg.kind_of?(String) ? arg.to_sym : arg set_or_return( :link_type, real_arg, :equal_to => [ :symbolic, :hard ] ) end def group(arg=nil) set_or_return( :group, arg, :regex => Chef::Config[:group_valid_regex] ) end def owner(arg=nil) set_or_return( :owner, arg, :regex => Chef::Config[:user_valid_regex] ) end # make link quack like a file (XXX: not for public consumption) def path target_file end private def verify_links_supported! # On certain versions of windows links are not supported. Make # sure we are not on such a platform. if Chef::Platform.windows? require 'chef/win32/file' begin Chef::ReservedNames::Win32::File.verify_links_supported! rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e Chef::Log.fatal("Link resource is not supported on this version of Windows") raise e end end end end end end chef-12.3.0/lib/chef/resource/windows_script.rb0000644000004100000410000000351412520074675021452 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 set_guard_inherited_attributes(:architecture) protected def initialize(name, run_context, resource_name, interpreter_command) super(name, run_context) @interpreter = interpreter_command @resource_name = resource_name @default_guard_interpreter = 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 end end end chef-12.3.0/lib/chef/resource/dpkg_package.rb0000644000004100000410000000174512520074675021000 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/package' require 'chef/provider/package/dpkg' class Chef class Resource class DpkgPackage < Chef::Resource::Package provides :dpkg_package, os: "linux" def initialize(name, run_context=nil) super @resource_name = :dpkg_package end end end end chef-12.3.0/lib/chef/resource/windows_package.rb0000644000004100000410000000441712520074675021544 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource/package' require 'chef/provider/package/windows' require 'chef/win32/error' if RUBY_PLATFORM =~ /mswin|mingw|windows/ class Chef class Resource class WindowsPackage < Chef::Resource::Package provides :package, os: "windows" provides :windows_package, os: "windows" def initialize(name, run_context=nil) super @allowed_actions.push(:install, :remove) @resource_name = :windows_package @source ||= source(@package_name) # Unique to this resource @installer_type = nil @timeout = 600 # In the past we accepted return code 127 for an unknown reason and 42 because of a bug @returns = [ 0 ] end def installer_type(arg=nil) set_or_return( :installer_type, arg, :kind_of => [ Symbol ] ) end def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => [ String, Integer ] ) end def returns(arg=nil) set_or_return( :returns, arg, :kind_of => [ String, Integer, Array ] ) end def source(arg=nil) if arg == nil && self.instance_variable_defined?(:@source) == true @source else raise ArgumentError, "Bad type for WindowsPackage resource, use a String" unless arg.is_a?(String) Chef::Log.debug("#{package_name}: sanitizing source path '#{arg}'") @source = ::File.absolute_path(arg).gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR) end end end end end chef-12.3.0/lib/chef/resource/smartos_package.rb0000644000004100000410000000205412520074675021535 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Copyright:: Copyright (c) 2010 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/package' require 'chef/provider/package/smartos' class Chef class Resource class SmartosPackage < Chef::Resource::Package provides :smartos_package provides :package, os: "solaris2", platform_family: "smartos" def initialize(name, run_context=nil) super @resource_name = :smartos_package end end end end chef-12.3.0/lib/chef/resource/macosx_service.rb0000644000004100000410000000277212520074675021413 0ustar www-datawww-data# # Author:: Mike Dodge () # Copyright:: Copyright (c) 2015 Facebook, 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/service' class Chef class Resource class MacosxService < Chef::Resource::Service provides :service, os: "darwin" provides :macosx_service, os: "darwin" identity_attr :service_name state_attrs :enabled, :running def initialize(name, run_context=nil) super @resource_name = :macosx_service @plist = nil @session_type = nil end # This will enable user to pass a plist in the case # that the filename and label for the service dont match def plist(arg=nil) set_or_return( :plist, arg, :kind_of => String ) end def session_type(arg=nil) set_or_return( :session_type, arg, :kind_of => String ) end end end end chef-12.3.0/lib/chef/resource/user.rb0000644000004100000410000000647312520074675017361 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource' class Chef class Resource class User < Chef::Resource identity_attr :username state_attrs :uid, :gid, :home provides :user def initialize(name, run_context=nil) super @resource_name = :user @username = name @comment = nil @uid = nil @gid = nil @home = nil @shell = nil @password = nil @system = false @manage_home = false @force = false @non_unique = false @action = :create @supports = { :manage_home => false, :non_unique => false } @iterations = 27855 @salt = nil @allowed_actions.push(:create, :remove, :modify, :manage, :lock, :unlock) end def username(arg=nil) set_or_return( :username, arg, :kind_of => [ String ] ) end def comment(arg=nil) set_or_return( :comment, arg, :kind_of => [ String ] ) end def uid(arg=nil) set_or_return( :uid, arg, :kind_of => [ String, Integer ] ) end def gid(arg=nil) set_or_return( :gid, arg, :kind_of => [ String, Integer ] ) end alias_method :group, :gid def home(arg=nil) set_or_return( :home, arg, :kind_of => [ String ] ) end def shell(arg=nil) set_or_return( :shell, arg, :kind_of => [ String ] ) end def password(arg=nil) set_or_return( :password, arg, :kind_of => [ String ] ) end def salt(arg=nil) set_or_return( :salt, arg, :kind_of => [ String ] ) end def iterations(arg=nil) set_or_return( :iterations, arg, :kind_of => [ Integer ] ) end def system(arg=nil) set_or_return( :system, arg, :kind_of => [ TrueClass, FalseClass ] ) end def manage_home(arg=nil) set_or_return( :manage_home, arg, :kind_of => [ TrueClass, FalseClass ] ) end def force(arg=nil) set_or_return( :force, arg, :kind_of => [ TrueClass, FalseClass ] ) end def non_unique(arg=nil) set_or_return( :non_unique, arg, :kind_of => [ TrueClass, FalseClass ] ) end end end end chef-12.3.0/lib/chef/resource/file.rb0000644000004100000410000000634012520074675017313 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Chisamore () # Copyright:: Copyright (c) 2008, 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 'chef/resource' require 'chef/platform/query_helpers' require 'chef/mixin/securable' require 'chef/resource/file/verification' class Chef class Resource class File < Chef::Resource include Chef::Mixin::Securable identity_attr :path if Platform.windows? # Use Windows rights instead of standard *nix permissions state_attrs :checksum, :rights, :deny_rights else state_attrs :checksum, :owner, :group, :mode end attr_writer :checksum provides :file def initialize(name, run_context=nil) super @resource_name = :file @path = name @backup = 5 @action = "create" @allowed_actions.push(:create, :delete, :touch, :create_if_missing) @atomic_update = Chef::Config[:file_atomic_update] @force_unlink = false @manage_symlink_source = nil @diff = nil @verifications = [] end def content(arg=nil) set_or_return( :content, arg, :kind_of => String ) end def backup(arg=nil) set_or_return( :backup, arg, :kind_of => [ Integer, FalseClass ] ) end def checksum(arg=nil) set_or_return( :checksum, arg, :regex => /^[a-zA-Z0-9]{64}$/ ) end def path(arg=nil) set_or_return( :path, arg, :kind_of => String ) end def diff(arg=nil) set_or_return( :diff, arg, :kind_of => String ) end def atomic_update(arg=nil) set_or_return( :atomic_update, arg, :kind_of => [ TrueClass, FalseClass ] ) end def force_unlink(arg=nil) set_or_return( :force_unlink, arg, :kind_of => [ TrueClass, FalseClass ] ) end def manage_symlink_source(arg=nil) set_or_return( :manage_symlink_source, arg, :kind_of => [ TrueClass, FalseClass ] ) end def verify(command=nil, opts={}, &block) if ! (command.nil? || [String, Symbol].include?(command.class)) raise ArgumentError, "verify requires either a string, symbol, or a block" end if command || block_given? @verifications << Verification.new(self, command, opts, &block) else @verifications end end end end end chef-12.3.0/lib/chef/resource/mdadm.rb0000644000004100000410000000433412520074675017457 0ustar www-datawww-data# # Author:: Joe Williams () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Joe Williams # 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' class Chef class Resource class Mdadm < Chef::Resource identity_attr :raid_device state_attrs :devices, :level, :chunk provides :mdadm def initialize(name, run_context=nil) super @resource_name = :mdadm @chunk = 16 @devices = [] @exists = false @level = 1 @metadata = "0.90" @bitmap = nil @raid_device = name @action = :create @allowed_actions.push(:create, :assemble, :stop) end def chunk(arg=nil) set_or_return( :chunk, arg, :kind_of => [ Integer ] ) end def devices(arg=nil) set_or_return( :devices, arg, :kind_of => [ Array ] ) end def exists(arg=nil) set_or_return( :exists, arg, :kind_of => [ TrueClass, FalseClass ] ) end def level(arg=nil) set_or_return( :level, arg, :kind_of => [ Integer ] ) end def metadata(arg=nil) set_or_return( :metadata, arg, :kind_of => [ String ] ) end def bitmap(arg=nil) set_or_return( :bitmap, arg, :kind_of => [ String ] ) end def raid_device(arg=nil) set_or_return( :raid_device, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/powershell_script.rb0000644000004100000410000000330012520074675022135 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 PowershellScript < Chef::Resource::WindowsScript provides :powershell_script, os: "windows" def initialize(name, run_context=nil) super(name, run_context, :powershell_script, "powershell.exe") @convert_boolean_return = false end def convert_boolean_return(arg=nil) set_or_return( :convert_boolean_return, arg, :kind_of => [ FalseClass, TrueClass ] ) end # Allow callers evaluating guards to request default # attribute values. This is needed to allow # convert_boolean_return to be true in guard context by default, # and false by default otherwise. When this mode becomes the # default for this resource, this method can be removed since # guard context and recipe resource context will have the # same behavior. def self.get_default_attributes(opts) {:convert_boolean_return => true} end end end end chef-12.3.0/lib/chef/resource/script.rb0000644000004100000410000000411112520074675017672 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource/execute' require 'chef/provider/script' class Chef class Resource class Script < Chef::Resource::Execute # Chef-13: go back to using :name as the identity attr identity_attr :command def initialize(name, run_context=nil) super @resource_name = :script # Chef-13: the command variable should be initialized to nil @command = name @code = nil @interpreter = nil @flags = nil @default_guard_interpreter = :default end def command(arg=nil) unless arg.nil? # Chef-13: change this to raise if the user is trying to set a value here Chef::Log.warn "Specifying command attribute on a script resource is a coding error, use the 'code' attribute, or the execute resource" Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef-13" end super end def code(arg=nil) set_or_return( :code, arg, :kind_of => [ String ] ) end def interpreter(arg=nil) set_or_return( :interpreter, arg, :kind_of => [ String ] ) end def flags(arg=nil) set_or_return( :flags, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/cron.rb0000644000004100000410000001164512520074675017341 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Bryan McLellan # 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' class Chef class Resource class Cron < Chef::Resource identity_attr :command state_attrs :minute, :hour, :day, :month, :weekday, :user provides :cron def initialize(name, run_context=nil) super @resource_name = :cron @action = :create @allowed_actions.push(:create, :delete) @minute = "*" @hour = "*" @day = "*" @month = "*" @weekday = "*" @command = nil @user = "root" @mailto = nil @path = nil @shell = nil @home = nil @time = nil @environment = {} end def minute(arg=nil) if arg.is_a?(Integer) converted_arg = arg.to_s else converted_arg = arg end begin if integerize(arg) > 59 then raise RangeError end rescue ArgumentError end set_or_return( :minute, converted_arg, :kind_of => String ) end def hour(arg=nil) if arg.is_a?(Integer) converted_arg = arg.to_s else converted_arg = arg end begin if integerize(arg) > 23 then raise RangeError end rescue ArgumentError end set_or_return( :hour, converted_arg, :kind_of => String ) end def day(arg=nil) if arg.is_a?(Integer) converted_arg = arg.to_s else converted_arg = arg end begin if integerize(arg) > 31 then raise RangeError end rescue ArgumentError end set_or_return( :day, converted_arg, :kind_of => String ) end def month(arg=nil) if arg.is_a?(Integer) converted_arg = arg.to_s else converted_arg = arg end begin if integerize(arg) > 12 then raise RangeError end rescue ArgumentError end set_or_return( :month, converted_arg, :kind_of => String ) end def weekday(arg=nil) if arg.is_a?(Integer) converted_arg = arg.to_s else converted_arg = arg end begin error_message = "You provided '#{arg}' as a weekday, acceptable values are " error_message << Provider::Cron::WEEKDAY_SYMBOLS.map {|sym| ":#{sym.to_s}"}.join(', ') error_message << " and a string in crontab format" if (arg.is_a?(Symbol) && !Provider::Cron::WEEKDAY_SYMBOLS.include?(arg)) || (!arg.is_a?(Symbol) && integerize(arg) > 7) || (!arg.is_a?(Symbol) && integerize(arg) < 0) raise RangeError, error_message end rescue ArgumentError end set_or_return( :weekday, converted_arg, :kind_of => [String, Symbol] ) end def time(arg=nil) set_or_return( :time, arg, :equal_to => Chef::Provider::Cron::SPECIAL_TIME_VALUES ) end def mailto(arg=nil) set_or_return( :mailto, arg, :kind_of => String ) end def path(arg=nil) set_or_return( :path, arg, :kind_of => String ) end def home(arg=nil) set_or_return( :home, arg, :kind_of => String ) end def shell(arg=nil) set_or_return( :shell, arg, :kind_of => String ) end def command(arg=nil) set_or_return( :command, arg, :kind_of => String ) end def user(arg=nil) set_or_return( :user, arg, :kind_of => String ) end def environment(arg=nil) set_or_return( :environment, arg, :kind_of => Hash ) end private # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no. def integerize(integerish) Integer(integerish) rescue TypeError 0 end end end end chef-12.3.0/lib/chef/resource/log.rb0000644000004100000410000000401612520074675017153 0ustar www-datawww-data# # Author:: Cary Penniman () # Author:: Tyler Cloke () # 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 'chef/resource' require 'chef/provider/log' class Chef class Resource class Log < Chef::Resource identity_attr :message # Sends a string from a recipe to a log provider # # log "some string to log" do # level :info # (default) also supports :warn, :debug, and :error # end # # === Example # log "your string to log" # # or # # log "a debug string" { level :debug } # # Initialize log resource with a name as the string to log # # === Parameters # name:: Message to log # collection:: Collection of included recipes # node:: Node where resource will be used def initialize(name, run_context=nil) super @resource_name = :log @level = :info @action = :write @allowed_actions.push(:write) @message = name end def message(arg=nil) set_or_return( :message, arg, :kind_of => String ) end # Log level, one of :debug, :info, :warn, :error or :fatal def level(arg=nil) set_or_return( :level, arg, :equal_to => [ :debug, :info, :warn, :error, :fatal ] ) end end end end chef-12.3.0/lib/chef/resource/rpm_package.rb0000644000004100000410000000230112520074675020636 0ustar www-datawww-data# # Author:: Thomas Bishop () # Copyright:: Copyright (c) 2010 Thomas Bishop # 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/package' require 'chef/provider/package/rpm' class Chef class Resource class RpmPackage < Chef::Resource::Package provides :rpm_package, os: [ "linux", "aix" ] def initialize(name, run_context=nil) super @resource_name = :rpm_package @allow_downgrade = false end def allow_downgrade(arg=nil) set_or_return( :allow_downgrade, arg, :kind_of => [ TrueClass, FalseClass ] ) end end end end chef-12.3.0/lib/chef/resource/package.rb0000644000004100000410000000451412520074675017770 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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 'chef/resource' class Chef class Resource class Package < Chef::Resource identity_attr :package_name state_attrs :version, :options def initialize(name, run_context=nil) super @action = :install @allowed_actions.push(:install, :upgrade, :remove, :purge, :reconfig) @candidate_version = nil @options = nil @package_name = name @resource_name = :package @response_file = nil @response_file_variables = Hash.new @source = nil @version = nil @timeout = 900 end def package_name(arg=nil) set_or_return( :package_name, arg, :kind_of => [ String, Array ] ) end def version(arg=nil) set_or_return( :version, arg, :kind_of => [ String, Array ] ) end def response_file(arg=nil) set_or_return( :response_file, arg, :kind_of => [ String ] ) end def response_file_variables(arg=nil) set_or_return( :response_file_variables, arg, :kind_of => [ Hash ] ) end def source(arg=nil) set_or_return( :source, arg, :kind_of => [ String ] ) end def options(arg=nil) set_or_return( :options, arg, :kind_of => [ String ] ) end def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => [String, Integer] ) end end end end chef-12.3.0/lib/chef/resource/group.rb0000644000004100000410000000465412520074675017536 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tyler Cloke () # 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. # class Chef class Resource class Group < Chef::Resource identity_attr :group_name state_attrs :members provides :group def initialize(name, run_context=nil) super @resource_name = :group @group_name = name @gid = nil @members = [] @excluded_members = [] @action = :create @append = false @non_unique = false @allowed_actions.push(:create, :remove, :modify, :manage) end def group_name(arg=nil) set_or_return( :group_name, arg, :kind_of => [ String ] ) end def gid(arg=nil) set_or_return( :gid, arg, :kind_of => [ String, Integer ] ) end def members(arg=nil) converted_members = arg.is_a?(String) ? [].push(arg) : arg set_or_return( :members, converted_members, :kind_of => [ Array ] ) end alias_method :users, :members def excluded_members(arg=nil) converted_members = arg.is_a?(String) ? [].push(arg) : arg set_or_return( :excluded_members, converted_members, :kind_of => [ Array ] ) end def append(arg=nil) set_or_return( :append, arg, :kind_of => [ TrueClass, FalseClass ] ) end def system(arg=nil) set_or_return( :system, arg, :kind_of => [ TrueClass, FalseClass ] ) end def non_unique(arg=nil) set_or_return( :non_unique, arg, :kind_of => [ TrueClass, FalseClass ] ) end end end end chef-12.3.0/lib/chef/resource/windows_service.rb0000644000004100000410000000376612520074675021617 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource/service' class Chef class Resource class WindowsService < Chef::Resource::Service # Until #1773 is resolved, you need to manually specify the windows_service resource # to use action :configure_startup and attribute startup_type provides :service, os: "windows" provides :windows_service, os: "windows" identity_attr :service_name state_attrs :enabled, :running def initialize(name, run_context=nil) super @resource_name = :windows_service @allowed_actions.push(:configure_startup) @startup_type = :automatic @run_as_user = "" @run_as_password = "" end def startup_type(arg=nil) # Set-Service arguments are automatic and manual # Win32::Service returns 'auto start' or 'demand start' respectively, which the provider currently uses set_or_return( :startup_type, arg, :equal_to => [ :automatic, :manual, :disabled ] ) end def run_as_user(arg=nil) set_or_return( :run_as_user, arg, :kind_of => [ String ] ) end def run_as_password(arg=nil) set_or_return( :run_as_password, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/subversion.rb0000644000004100000410000000255312520074675020575 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # 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 "chef/resource/scm" class Chef class Resource class Subversion < Chef::Resource::Scm def initialize(name, run_context=nil) super @svn_arguments = '--no-auth-cache' @svn_info_args = '--no-auth-cache' @resource_name = :subversion allowed_actions << :force_export end # Override exception to strip password if any, so it won't appear in logs and different Chef notifications def custom_exception_message(e) "#{self} (#{defined_at}) had an error: #{e.class.name}: #{svn_password ? e.message.gsub(svn_password, "[hidden_password]") : e.message}" end end end end chef-12.3.0/lib/chef/resource/paludis_package.rb0000644000004100000410000000213612520074675021507 0ustar www-datawww-data# # Author:: Vasiliy Tolstov () # Copyright:: Copyright (c) 2014 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/package' require 'chef/provider/package/paludis' class Chef class Resource class PaludisPackage < Chef::Resource::Package provides :paludis_package, os: "linux" def initialize(name, run_context=nil) super(name, run_context) @resource_name = :paludis_package @allowed_actions.push(:install, :remove, :upgrade) @timeout = 3600 end end end end chef-12.3.0/lib/chef/resource/timestamped_deploy.rb0000644000004100000410000000175412520074675022270 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource # Convenience class for using the deploy resource with the timestamped # deployment strategy (provider) class TimestampedDeploy < Chef::Resource::Deploy provides :timestamped_deploy def initialize(*args, &block) super(*args, &block) end end end end chef-12.3.0/lib/chef/resource/bash.rb0000644000004100000410000000170112520074675017305 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/script' require 'chef/provider/script' class Chef class Resource class Bash < Chef::Resource::Script def initialize(name, run_context=nil) super @resource_name = :bash @interpreter = "bash" end end end end chef-12.3.0/lib/chef/resource/reboot.rb0000644000004100000410000000266612520074675017675 0ustar www-datawww-data# # Author:: Chris Doherty ) # Copyright:: Copyright (c) 2014 Chef, 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' # In using this resource via notifications, it's important to *only* use # immediate notifications. Delayed notifications produce unintuitive and # probably undesired results. class Chef class Resource class Reboot < Chef::Resource def initialize(name, run_context=nil) super @resource_name = :reboot @provider = Chef::Provider::Reboot @allowed_actions.push(:request_reboot, :reboot_now, :cancel) @reason = "Reboot by Chef" @delay_mins = 0 # no default action. end def reason(arg=nil) set_or_return(:reason, arg, :kind_of => String) end def delay_mins(arg=nil) set_or_return(:delay_mins, arg, :kind_of => Fixnum) end end end end chef-12.3.0/lib/chef/resource/homebrew_package.rb0000644000004100000410000000241212520074675021653 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Graeme Mathieson () # # Copyright 2011-2013, Opscode, Inc. # Copyright 2014, Chef Software, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/provider/package' require 'chef/resource/package' class Chef class Resource class HomebrewPackage < Chef::Resource::Package provides :homebrew_package provides :package, os: "darwin" def initialize(name, run_context=nil) super @resource_name = :homebrew_package @homebrew_user = nil end def homebrew_user(arg=nil) set_or_return( :homebrew_user, arg, :kind_of => [ String, Integer ] ) end end end end chef-12.3.0/lib/chef/resource/perl.rb0000644000004100000410000000170112520074675017332 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/script' require 'chef/provider/script' class Chef class Resource class Perl < Chef::Resource::Script def initialize(name, run_context=nil) super @resource_name = :perl @interpreter = "perl" end end end end chef-12.3.0/lib/chef/resource/remote_file.rb0000644000004100000410000001014712520074675020666 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Chisamore () # Copyright:: Copyright (c) 2008, 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 'uri' require 'chef/resource/file' require 'chef/provider/remote_file' require 'chef/mixin/securable' class Chef class Resource class RemoteFile < Chef::Resource::File include Chef::Mixin::Securable provides :remote_file def initialize(name, run_context=nil) super @resource_name = :remote_file @action = "create" @source = [] @use_etag = true @use_last_modified = true @ftp_active_mode = false @headers = {} @provider = Chef::Provider::RemoteFile end # source can take any of the following as arguments # - A single string argument # - Multiple string arguments # - An array or strings # - A delayed evaluator that evaluates to a string # or array of strings # All strings must be parsable as URIs. # source returns an array of strings. def source(*args) arg = parse_source_args(args) ret = set_or_return(:source, arg, { :callbacks => { :validate_source => method(:validate_source) }}) if ret.is_a? String Array(ret) else ret end end def parse_source_args(args) if args.empty? nil elsif args[0].is_a?(Chef::DelayedEvaluator) && args.count == 1 args[0] elsif args.any? {|a| a.is_a?(Chef::DelayedEvaluator)} && args.count > 1 raise Exceptions::InvalidRemoteFileURI, "Only 1 source argument allowed when using a lazy evaluator" else Array(args).flatten end end def checksum(args=nil) set_or_return( :checksum, args, :kind_of => String ) end # Disable or enable ETag and Last Modified conditional GET. Equivalent to # use_etag(true_or_false) # use_last_modified(true_or_false) def use_conditional_get(true_or_false) use_etag(true_or_false) use_last_modified(true_or_false) end def use_etag(args=nil) set_or_return( :use_etag, args, :kind_of => [ TrueClass, FalseClass ] ) end alias :use_etags :use_etag def use_last_modified(args=nil) set_or_return( :use_last_modified, args, :kind_of => [ TrueClass, FalseClass ] ) end def ftp_active_mode(args=nil) set_or_return( :ftp_active_mode, args, :kind_of => [ TrueClass, FalseClass ] ) end def headers(args=nil) set_or_return( :headers, args, :kind_of => Hash ) end private def validate_source(source) source = Array(source).flatten raise ArgumentError, "#{resource_name} has an empty source" if source.empty? source.each do |src| unless absolute_uri?(src) raise Exceptions::InvalidRemoteFileURI, "#{src.inspect} is not a valid `source` parameter for #{resource_name}. `source` must be an absolute URI or an array of URIs." end end true end def absolute_uri?(source) source.kind_of?(String) and URI.parse(source).absolute? rescue URI::InvalidURIError false end end end end chef-12.3.0/lib/chef/resource/env.rb0000644000004100000410000000303012520074675017155 0ustar www-datawww-data# # Author:: Doug MacEachern () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2010 VMware, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource class Env < Chef::Resource identity_attr :key_name state_attrs :value provides :env, os: "windows" def initialize(name, run_context=nil) super @resource_name = :env @key_name = name @value = nil @action = :create @delim = nil @allowed_actions.push(:create, :delete, :modify) end def key_name(arg=nil) set_or_return( :key_name, arg, :kind_of => [ String ] ) end def value(arg=nil) set_or_return( :value, arg, :kind_of => [ String ] ) end def delim(arg=nil) set_or_return( :delim, arg, :kind_of => [ String ] ) end end end end chef-12.3.0/lib/chef/resource/deploy.rb0000644000004100000410000002743712520074675017702 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # 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. # # EX: # deploy "/my/deploy/dir" do # repo "git@github.com/whoami/project" # revision "abc123" # or "HEAD" or "TAG_for_1.0" or (subversion) "1234" # user "deploy_ninja" # enable_submodules true # migrate true # migration_command "rake db:migrate" # environment "RAILS_ENV" => "production", "OTHER_ENV" => "foo" # shallow_clone true # action :deploy # or :rollback # restart_command "touch tmp/restart.txt" # git_ssh_wrapper "wrap-ssh4git.sh" # scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion # svn_username "whoami" # svn_password "supersecret" # end require "chef/resource/scm" class Chef class Resource # Deploy: Deploy apps from a source control repository. # # Callbacks: # Callbacks can be a block or a string. If given a block, the code # is evaluated as an embedded recipe, and run at the specified # point in the deploy process. If given a string, the string is taken as # a path to a callback file/recipe. Paths are evaluated relative to the # release directory. Callback files can contain chef code (resources, etc.) # class Deploy < Chef::Resource provider_base Chef::Provider::Deploy identity_attr :repository state_attrs :deploy_to, :revision def initialize(name, run_context=nil) super @resource_name = :deploy @deploy_to = name @environment = nil @repository_cache = 'cached-copy' @copy_exclude = [] @purge_before_symlink = %w{log tmp/pids public/system} @create_dirs_before_symlink = %w{tmp public config} @symlink_before_migrate = {"config/database.yml" => "config/database.yml"} @symlinks = {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"} @revision = 'HEAD' @action = :deploy @migrate = false @rollback_on_error = false @remote = "origin" @enable_submodules = false @shallow_clone = false @scm_provider = Chef::Provider::Git @svn_force_export = false @allowed_actions.push(:force_deploy, :deploy, :rollback) @additional_remotes = Hash[] @keep_releases = 5 @enable_checkout = true @checkout_branch = "deploy" end # where the checked out/cloned code goes def destination @destination ||= shared_path + "/#{@repository_cache}" end # where shared stuff goes, i.e., logs, tmp, etc. goes here def shared_path @shared_path ||= @deploy_to + "/shared" end # where the deployed version of your code goes def current_path @current_path ||= @deploy_to + "/current" end def depth @shallow_clone ? "5" : nil end # note: deploy_to is your application "meta-root." def deploy_to(arg=nil) set_or_return( :deploy_to, arg, :kind_of => [ String ] ) end def repo(arg=nil) set_or_return( :repo, arg, :kind_of => [ String ] ) end alias :repository :repo def remote(arg=nil) set_or_return( :remote, arg, :kind_of => [ String ] ) end def role(arg=nil) set_or_return( :role, arg, :kind_of => [ String ] ) end def restart_command(arg=nil, &block) arg ||= block set_or_return( :restart_command, arg, :kind_of => [ String, Proc ] ) end alias :restart :restart_command def migrate(arg=nil) set_or_return( :migrate, arg, :kind_of => [ TrueClass, FalseClass ] ) end def migration_command(arg=nil) set_or_return( :migration_command, arg, :kind_of => [ String ] ) end def rollback_on_error(arg=nil) set_or_return( :rollback_on_error, arg, :kind_of => [ TrueClass, FalseClass ] ) end def user(arg=nil) set_or_return( :user, arg, :kind_of => [ String ] ) end def group(arg=nil) set_or_return( :group, arg, :kind_of => [ String ] ) end def enable_submodules(arg=nil) set_or_return( :enable_submodules, arg, :kind_of => [ TrueClass, FalseClass ] ) end def shallow_clone(arg=nil) set_or_return( :shallow_clone, arg, :kind_of => [ TrueClass, FalseClass ] ) end def repository_cache(arg=nil) set_or_return( :repository_cache, arg, :kind_of => [ String ] ) end def copy_exclude(arg=nil) set_or_return( :copy_exclude, arg, :kind_of => [ String ] ) end def revision(arg=nil) set_or_return( :revision, arg, :kind_of => [ String ] ) end alias :branch :revision def git_ssh_wrapper(arg=nil) set_or_return( :git_ssh_wrapper, arg, :kind_of => [ String ] ) end alias :ssh_wrapper :git_ssh_wrapper def svn_username(arg=nil) set_or_return( :svn_username, arg, :kind_of => [ String ] ) end def svn_password(arg=nil) set_or_return( :svn_password, arg, :kind_of => [ String ] ) end def svn_arguments(arg=nil) set_or_return( :svn_arguments, arg, :kind_of => [ String ] ) end def svn_info_args(arg=nil) set_or_return( :svn_arguments, arg, :kind_of => [ String ]) end def scm_provider(arg=nil) klass = if arg.kind_of?(String) || arg.kind_of?(Symbol) lookup_provider_constant(arg) else arg end set_or_return( :scm_provider, klass, :kind_of => [ Class ] ) end def svn_force_export(arg=nil) set_or_return( :svn_force_export, arg, :kind_of => [ TrueClass, FalseClass ] ) end def environment(arg=nil) if arg.is_a?(String) Chef::Log.debug "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'" Chef::Log.warn "[DEPRECATED] please modify your deploy recipe or attributes to set the environment using a hash" arg = {"RAILS_ENV"=>arg,"MERB_ENV"=>arg,"RACK_ENV"=>arg} end set_or_return( :environment, arg, :kind_of => [ Hash ] ) end # The number of old release directories to keep around after cleanup def keep_releases(arg=nil) [set_or_return( :keep_releases, arg, :kind_of => [ Integer ]), 1].max end # An array of paths, relative to your app's root, to be purged from a # SCM clone/checkout before symlinking. Use this to get rid of files and # directories you want to be shared between releases. # Default: ["log", "tmp/pids", "public/system"] def purge_before_symlink(arg=nil) set_or_return( :purge_before_symlink, arg, :kind_of => Array ) end # An array of paths, relative to your app's root, where you expect dirs to # exist before symlinking. This runs after #purge_before_symlink, so you # can use this to recreate dirs that you had previously purged. # For example, if you plan to use a shared directory for pids, and you # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp, # then specify tmp here so that the tmp directory will exist when you # symlink the pids directory in to the current release. # Default: ["tmp", "public", "config"] def create_dirs_before_symlink(arg=nil) set_or_return( :create_dirs_before_symlink, arg, :kind_of => Array ) end # A Hash of shared/dir/path => release/dir/path. This attribute determines # which files and dirs in the shared directory get symlinked to the current # release directory, and where they go. If you have a directory # $shared/pids that you would like to symlink as $current_release/tmp/pids # you specify it as "pids" => "tmp/pids" # Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"} def symlinks(arg=nil) set_or_return( :symlinks, arg, :kind_of => Hash ) end # A Hash of shared/dir/path => release/dir/path. This attribute determines # which files in the shared directory get symlinked to the current release # directory and where they go. Unlike map_shared_files, these are symlinked # *before* any migration is run. # For a rails/merb app, this is used to link in a known good database.yml # (with the production db password) before running migrate. # Default {"config/database.yml" => "config/database.yml"} def symlink_before_migrate(arg=nil) set_or_return( :symlink_before_migrate, arg, :kind_of => Hash ) end # Callback fires before migration is run. def before_migrate(arg=nil, &block) arg ||= block set_or_return(:before_migrate, arg, :kind_of => [Proc, String]) end # Callback fires before symlinking def before_symlink(arg=nil, &block) arg ||= block set_or_return(:before_symlink, arg, :kind_of => [Proc, String]) end # Callback fires before restart def before_restart(arg=nil, &block) arg ||= block set_or_return(:before_restart, arg, :kind_of => [Proc, String]) end # Callback fires after restart def after_restart(arg=nil, &block) arg ||= block set_or_return(:after_restart, arg, :kind_of => [Proc, String]) end def additional_remotes(arg=nil) set_or_return( :additional_remotes, arg, :kind_of => Hash ) end def enable_checkout(arg=nil) set_or_return( :enable_checkout, arg, :kind_of => [TrueClass, FalseClass] ) end def checkout_branch(arg=nil) set_or_return( :checkout_branch, arg, :kind_of => String ) end # FIXME The Deploy resource may be passed to an SCM provider as its # resource. The SCM provider knows that SCM resources can specify a # timeout for SCM operations. The deploy resource must therefore support # a timeout method, but the timeout it describes is for SCM operations, # not the overall deployment. This is potentially confusing. def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => Integer ) end end end end chef-12.3.0/lib/chef/resource/route.rb0000644000004100000410000000556512520074675017542 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2009 Bryan McLellan # 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' class Chef class Resource class Route < Chef::Resource identity_attr :target state_attrs :netmask, :gateway def initialize(name, run_context=nil) super @resource_name = :route @target = name @action = [:add] @allowed_actions.push(:add, :delete) @netmask = nil @gateway = nil @metric = nil @device = nil @route_type = :host @networking = nil @networking_ipv6 = nil @hostname = nil @domainname = nil @domain = nil end def networking(arg=nil) set_or_return( :networking, arg, :kind_of => String ) end def networking_ipv6(arg=nil) set_or_return( :networking_ipv6, arg, :kind_of => String ) end def hostname(arg=nil) set_or_return( :hostname, arg, :kind_of => String ) end def domainname(arg=nil) set_or_return( :domainname, arg, :kind_of => String ) end def domain(arg=nil) set_or_return( :domain, arg, :kind_of => String ) end def target(arg=nil) set_or_return( :target, arg, :kind_of => String ) end def netmask(arg=nil) set_or_return( :netmask, arg, :kind_of => String ) end def gateway(arg=nil) set_or_return( :gateway, arg, :kind_of => String ) end def metric(arg=nil) set_or_return( :metric, arg, :kind_of => Integer ) end def device(arg=nil) set_or_return( :device, arg, :kind_of => String ) end def route_type(arg=nil) real_arg = arg.kind_of?(String) ? arg.to_sym : arg set_or_return( :route_type, real_arg, :equal_to => [ :host, :net ] ) end end end end chef-12.3.0/lib/chef/resource/service.rb0000644000004100000410000001143512520074675020035 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Tyler Cloke () # 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 'chef/resource' class Chef class Resource class Service < Chef::Resource identity_attr :service_name state_attrs :enabled, :running def initialize(name, run_context=nil) super @resource_name = :service @service_name = name @enabled = nil @running = nil @parameters = nil @pattern = service_name @start_command = nil @stop_command = nil @status_command = nil @restart_command = nil @reload_command = nil @init_command = nil @priority = nil @timeout = nil @action = "nothing" @supports = { :restart => false, :reload => false, :status => false } @allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload) end def service_name(arg=nil) set_or_return( :service_name, arg, :kind_of => [ String ] ) end # regex for match against ps -ef when !supports[:has_status] && status == nil def pattern(arg=nil) set_or_return( :pattern, arg, :kind_of => [ String ] ) end # command to call to start service def start_command(arg=nil) set_or_return( :start_command, arg, :kind_of => [ String ] ) end # command to call to stop service def stop_command(arg=nil) set_or_return( :stop_command, arg, :kind_of => [ String ] ) end # command to call to get status of service def status_command(arg=nil) set_or_return( :status_command, arg, :kind_of => [ String ] ) end # command to call to restart service def restart_command(arg=nil) set_or_return( :restart_command, arg, :kind_of => [ String ] ) end def reload_command(arg=nil) set_or_return( :reload_command, arg, :kind_of => [ String ] ) end # The path to the init script associated with the service. On many # distributions this is '/etc/init.d/SERVICE_NAME' by default. In # non-standard configurations setting this value will save having to # specify overrides for the start_command, stop_command and # restart_command attributes. def init_command(arg=nil) set_or_return( :init_command, arg, :kind_of => [ String ] ) end # if the service is enabled or not def enabled(arg=nil) set_or_return( :enabled, arg, :kind_of => [ TrueClass, FalseClass ] ) end # if the service is running or not def running(arg=nil) set_or_return( :running, arg, :kind_of => [ TrueClass, FalseClass ] ) end # Priority arguments can have two forms: # # - a simple number, in which the default start runlevels get # that as the start value and stop runlevels get 100 - value. # # - a hash like { 2 => [:start, 20], 3 => [:stop, 55] }, where # the service will be marked as started with priority 20 in # runlevel 2, stopped in 3 with priority 55 and no symlinks or # similar for other runlevels # def priority(arg=nil) set_or_return( :priority, arg, :kind_of => [ Integer, String, Hash ] ) end # timeout only applies to the windows service manager def timeout(arg=nil) set_or_return( :timeout, arg, :kind_of => Integer ) end def parameters(arg=nil) set_or_return( :parameters, arg, :kind_of => [ Hash ] ) end def supports(args={}) if args.is_a? Array args.each { |arg| @supports[arg] = true } elsif args.any? @supports = args else @supports end end end end end chef-12.3.0/lib/chef/resource/conditional_action_not_nothing.rb0000644000004100000410000000223712520074675024643 0ustar www-datawww-data# # Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Resource class ConditionalActionNotNothing attr_reader :current_action def initialize(current_action) @current_action = current_action end def continue? # @positivity == not_if @current_action != :nothing end def short_description description end def description "action :nothing" end def to_text "not_if { action == :nothing }" end end end end chef-12.3.0/lib/chef/resource/template.rb0000644000004100000410000001670312520074675020213 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Chisamore () # Author:: Tyler Cloke () # Copyright:: Copyright (c) 2008, 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 'chef/resource/file' require 'chef/provider/template' require 'chef/mixin/securable' class Chef class Resource class Template < Chef::Resource::File include Chef::Mixin::Securable provides :template attr_reader :inline_helper_blocks attr_reader :inline_helper_modules def initialize(name, run_context=nil) super @resource_name = :template @action = "create" @source = "#{::File.basename(name)}.erb" @cookbook = nil @local = false @variables = Hash.new @inline_helper_blocks = {} @inline_helper_modules = [] @helper_modules = [] end def source(file=nil) set_or_return( :source, file, :kind_of => [ String, Array ] ) end def variables(args=nil) set_or_return( :variables, args, :kind_of => [ Hash ] ) end def cookbook(args=nil) set_or_return( :cookbook, args, :kind_of => [ String ] ) end def local(args=nil) set_or_return( :local, args, :kind_of => [ TrueClass, FalseClass ] ) end # Declares a helper method to be defined in the template context when # rendering. # # === Example: # # ==== Basic usage: # Given the following helper: # helper(:static_value) { "hello from helper" } # A template with the following code: # <%= static_value %> # Will render as; # hello from helper # # ==== Referencing Instance Variables: # Any instance variables available to the template can be referenced in # the method body. For example, you can simplify accessing app-specific # node attributes like this: # helper(:app) { @node[:my_app_attributes] } # And use it in a template like this: # <%= app[:listen_ports] %> # This is equivalent to the non-helper template code: # <%= @node[:my_app_attributes][:listen_ports] %> # # ==== Method Arguments: # Helper methods can also take arguments. The syntax available for # argument specification supports full syntax available for method # definition. # # Continuing the above example of simplifying attribute access, we can # define a helper to look up app-specific attributes like this: # helper(:app) { |setting| @node[:my_app_attributes][setting] } # The template can then look up attributes like this: # <%= app(:listen_ports) %> def helper(method_name, &block) unless block_given? raise Exceptions::ValidationFailed, "`helper(:method)` requires a block argument (e.g., `helper(:method) { code }`)" end unless method_name.kind_of?(Symbol) raise Exceptions::ValidationFailed, "method_name argument to `helper(method_name)` must be a symbol (e.g., `helper(:method) { code }`)" end @inline_helper_blocks[method_name] = block end # Declares a module to define helper methods in the template's context # when rendering. There are two primary forms. # # === Inline Module Definition # When a block is given, the block is used to define a module which is # then mixed in to the template context w/ `extend`. # # ==== Inline Module Example # Given the following code in the template resource: # helpers do # # Add "syntax sugar" for referencing app-specific attributes # def app(attribute) # @node[:my_app_attributes][attribute] # end # end # You can use it in the template like so: # <%= app(:listen_ports) %> # Which is equivalent to: # <%= @node[:my_app_attributes][:listen_ports] %> # # === External Module Form # When a module name is given, the template context will be extended with # that module. This is the recommended way to customize template contexts # when you need to define more than an handful of helper functions (but # also try to keep your template helpers from getting out of hand--if you # have very complex logic in your template helpers, you should further # extract your code into separate libraries). # # ==== External Module Example # To extract the above inline module code to a library, you'd create a # library file like this: # module MyTemplateHelper # # Add "syntax sugar" for referencing app-specific attributes # def app(attribute) # @node[:my_app_attributes][attribute] # end # end # And in the template resource: # helpers(MyTemplateHelper) # The template code in the above example will work unmodified. def helpers(module_name=nil,&block) if block_given? and !module_name.nil? raise Exceptions::ValidationFailed, "Passing both a module and block to #helpers is not supported. Call #helpers multiple times instead" elsif block_given? @inline_helper_modules << block elsif module_name.kind_of?(::Module) @helper_modules << module_name elsif module_name.nil? raise Exceptions::ValidationFailed, "#helpers requires either a module name or inline module code as a block.\n" + "e.g.: helpers do; helper_code; end;\n" + "OR: helpers(MyHelpersModule)" else raise Exceptions::ValidationFailed, "Argument to #helpers must be a module. You gave #{module_name.inspect} (#{module_name.class})" end end # Compiles all helpers from inline method definitions, inline module # definitions, and external modules into an Array of Modules. The context # object for the template is extended with these modules to provide # per-resource template logic. def helper_modules compiled_helper_methods + compiled_helper_modules + @helper_modules end private # compiles helper methods into a module that can be included in template context def compiled_helper_methods if inline_helper_blocks.empty? [] else resource_helper_blocks = inline_helper_blocks helper_mod = Module.new do resource_helper_blocks.each do |method_name, method_body| define_method(method_name, &method_body) end end [ helper_mod ] end end def compiled_helper_modules @inline_helper_modules.map do |module_body| Module.new(&module_body) end end end end end chef-12.3.0/lib/chef/resource/batch.rb0000644000004100000410000000170512520074675017455 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 provides :batch, os: "windows" def initialize(name, run_context=nil) super(name, run_context, :batch, "cmd.exe") end end end end chef-12.3.0/lib/chef/resource/csh.rb0000644000004100000410000000167612520074675017160 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/resource/script' require 'chef/provider/script' class Chef class Resource class Csh < Chef::Resource::Script def initialize(name, run_context=nil) super @resource_name = :csh @interpreter = "csh" end end end end chef-12.3.0/lib/chef/run_lock.rb0000644000004100000410000001246712520074675016370 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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/mixin/create_path' require 'fcntl' if Chef::Platform.windows? require 'chef/win32/mutex' end require 'chef/config' require 'chef/exceptions' require 'timeout' class Chef # == Chef::RunLock # Provides an interface for acquiring and releasing a system-wide exclusive # lock. # # Used by Chef::Client to ensure only one instance of chef-client (or solo) # is modifying the system at a time. class RunLock include Chef::Mixin::CreatePath attr_reader :runlock attr_reader :mutex attr_reader :runlock_file # Create a new instance of RunLock # === Arguments # * :lockfile::: the full path to the lockfile. def initialize(lockfile) @runlock_file = lockfile @runlock = nil @mutex = nil @runpid = nil end # Acquire the system-wide lock. Will block indefinitely if another process # already has the lock and Chef::Config[:run_lock_timeout] is # not set. Otherwise will block for Chef::Config[:run_lock_timeout] # seconds and exit if the lock is not acquired. # # Each call to acquire should have a corresponding call to #release. # # The implementation is based on File#flock (see also: flock(2)). # # Either acquire() or test() methods should be called in order to # get the ownership of run_lock. def acquire if timeout_given? begin Timeout::timeout(time_to_wait) do unless test if time_to_wait > 0.0 wait else exit_from_timeout end end end rescue Timeout::Error => e exit_from_timeout end else wait unless test end end # # Tests and if successful acquires the system-wide lock. # Returns true if the lock is acquired, false otherwise. # # Either acquire() or test() methods should be called in order to # get the ownership of run_lock. def test # ensure the runlock_file path exists create_path(File.dirname(runlock_file)) @runlock = File.open(runlock_file,'a+') if Chef::Platform.windows? acquire_win32_mutex else # If we support FD_CLOEXEC, then use it. # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not # ruby-1.8.7/1.9.3 if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC') runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC) end # Flock will return 0 if it can acquire the lock otherwise it # will return false if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0 true else false end end end # # Waits until acquiring the system-wide lock. # def wait Chef::Log.warn("Chef client #{runpid} is running, will wait for it to finish and then run.") if Chef::Platform.windows? mutex.wait else runlock.flock(File::LOCK_EX) end end def save_pid runlock.truncate(0) runlock.rewind # truncate doesn't reset position to 0. runlock.write(Process.pid.to_s) # flush the file fsync flushes the system buffers # in addition to ruby buffers runlock.fsync end # Release the system-wide lock. def release if runlock if Chef::Platform.windows? mutex.release else runlock.flock(File::LOCK_UN) end runlock.close # Don't unlink the pid file, if another chef-client was waiting, it # won't be recreated. Better to leave a "dead" pid file than not have # it available if you need to break the lock. reset end end private def reset @runlock = nil @mutex = nil @runpid = nil end # Since flock mechanism doesn't exist on windows we are using # platform Mutex. # We are creating a "Global" mutex here so that non-admin # users can not DoS chef-client by creating the same named # mutex we are using locally. # Mutex name is case-sensitive contrary to other things in # windows. "\" is the only invalid character. def acquire_win32_mutex @mutex = Chef::ReservedNames::Win32::Mutex.new("Global\\#{runlock_file.gsub(/[\\]/, "/").downcase}") mutex.test end def runpid @runpid ||= runlock.read.strip end def timeout_given? !time_to_wait.nil? end def time_to_wait Chef::Config[:run_lock_timeout] end def exit_from_timeout rp = runpid release # Just to be on the safe side... raise Chef::Exceptions::RunLockTimeout.new(time_to_wait, rp) end end end chef-12.3.0/lib/chef/cookbook_site_streaming_uploader.rb0000644000004100000410000002144312520074675023344 0ustar www-datawww-data# # Author:: Stanislav Vitvitskiy # Author:: Nuo Yan (nuo@opscode.com) # Author:: Christopher Walters () # Copyright:: Copyright (c) 2009, 2010 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 'uri' require 'net/http' require 'mixlib/authentication/signedheaderauth' require 'openssl' class Chef # == Chef::CookbookSiteStreamingUploader # A streaming multipart HTTP upload implementation. Used to upload cookbooks # (in tarball form) to http://cookbooks.opscode.com # # inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html class CookbookSiteStreamingUploader DefaultHeaders = { 'accept' => 'application/json', 'x-chef-version' => ::Chef::VERSION } class << self def create_build_dir(cookbook) tmp_cookbook_path = Tempfile.new("chef-#{cookbook.name}-build") tmp_cookbook_path.close tmp_cookbook_dir = tmp_cookbook_path.path File.unlink(tmp_cookbook_dir) FileUtils.mkdir_p(tmp_cookbook_dir) Chef::Log.debug("Staging at #{tmp_cookbook_dir}") checksums_to_on_disk_paths = cookbook.checksums Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment| cookbook.manifest[segment].each do |manifest_record| path_in_cookbook = manifest_record[:path] on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]] dest = File.join(tmp_cookbook_dir, cookbook.name.to_s, path_in_cookbook) FileUtils.mkdir_p(File.dirname(dest)) Chef::Log.debug("Staging #{on_disk_path} to #{dest}") FileUtils.cp(on_disk_path, dest) end end # First, generate metadata Chef::Log.debug("Generating metadata") kcm = Chef::Knife::CookbookMetadata.new kcm.config[:cookbook_path] = [ tmp_cookbook_dir ] kcm.name_args = [ cookbook.name.to_s ] kcm.run tmp_cookbook_dir end def post(to_url, user_id, secret_key_filename, params = {}, headers = {}) make_request(:post, to_url, user_id, secret_key_filename, params, headers) end def put(to_url, user_id, secret_key_filename, params = {}, headers = {}) make_request(:put, to_url, user_id, secret_key_filename, params, headers) end def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {}) boundary = '----RubyMultipartClient' + rand(1000000).to_s + 'ZZZZZ' parts = [] content_file = nil timestamp = Time.now.utc.iso8601 secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename)) unless params.nil? || params.empty? params.each do |key, value| if value.kind_of?(File) content_file = value filepath = value.path filename = File.basename(filepath) parts << StringPart.new( "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" + "Content-Type: application/octet-stream\r\n\r\n") parts << StreamPart.new(value, File.size(filepath)) parts << StringPart.new("\r\n") else parts << StringPart.new( "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n") parts << StringPart.new(value.to_s + "\r\n") end end parts << StringPart.new("--" + boundary + "--\r\n") end body_stream = MultipartStream.new(parts) timestamp = Time.now.utc.iso8601 url = URI.parse(to_url) Chef::Log.logger.debug("Signing: method: #{http_verb}, path: #{url.path}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}") # We use the body for signing the request if the file parameter # wasn't a valid file or wasn't included. Extract the body (with # multi-part delimiters intact) to sign the request. # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and # always hash the entire request body. In the file case it would just be # expanded multipart text - the entire body of the POST. content_body = parts.inject("") { |result,part| result + part.read(0, part.size) } content_file.rewind if content_file # we consumed the file for the above operation, so rewind it. signing_options = { :http_method=>http_verb, :path=>url.path, :user_id=>user_id, :timestamp=>timestamp} (content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || "")) headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key)) content_file.rewind if content_file # net/http doesn't like symbols for header keys, so we'll to_s each one just in case headers = DefaultHeaders.merge(Hash[*headers.map{ |k,v| [k.to_s, v] }.flatten]) req = case http_verb when :put Net::HTTP::Put.new(url.path, headers) when :post Net::HTTP::Post.new(url.path, headers) end req.content_length = body_stream.size req.content_type = 'multipart/form-data; boundary=' + boundary unless parts.empty? req.body_stream = body_stream http = Net::HTTP.new(url.host, url.port) if url.scheme == "https" http.use_ssl = true http.verify_mode = verify_mode end res = http.request(req) #res = http.start {|http_proc| http_proc.request(req) } # alias status to code and to_s to body for test purposes # TODO: stop the following madness! class << res alias :to_s :body # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[]) def headers self end def status code.to_i end end res end private def verify_mode verify_mode = Chef::Config[:ssl_verify_mode] if verify_mode == :verify_none OpenSSL::SSL::VERIFY_NONE elsif verify_mode == :verify_peer OpenSSL::SSL::VERIFY_PEER end end end class StreamPart def initialize(stream, size) @stream, @size = stream, size end def size @size end # read the specified amount from the stream def read(offset, how_much) @stream.read(how_much) end end class StringPart def initialize(str) @str = str end def size @str.length end # read the specified amount from the string startiung at the offset def read(offset, how_much) @str[offset, how_much] end end class MultipartStream def initialize(parts) @parts = parts @part_no = 0 @part_offset = 0 end def size @parts.inject(0) {|size, part| size + part.size} end def read(how_much, dst_buf = nil) if @part_no >= @parts.size dst_buf.replace('') if dst_buf return dst_buf end how_much_current_part = @parts[@part_no].size - @part_offset how_much_current_part = if how_much_current_part > how_much how_much else how_much_current_part end how_much_next_part = how_much - how_much_current_part current_part = @parts[@part_no].read(@part_offset, how_much_current_part) # recurse into the next part if the current one was not large enough if how_much_next_part > 0 @part_no += 1 @part_offset = 0 next_part = read(how_much_next_part) result = current_part + if next_part next_part else '' end else @part_offset += how_much_current_part result = current_part end dst_buf ? dst_buf.replace(result || '') : result end end end end chef-12.3.0/lib/chef/resource_collection/0000755000004100000410000000000012520074675020257 5ustar www-datawww-datachef-12.3.0/lib/chef/resource_collection/resource_collection_serialization.rb0000644000004100000410000000333312520074675027605 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class ResourceCollection module ResourceCollectionSerialization # Serialize this object as a hash def to_hash instance_vars = Hash.new self.instance_variables.each do |iv| instance_vars[iv] = self.instance_variable_get(iv) end { 'json_class' => self.class.name, 'instance_vars' => instance_vars } end def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def self.included(base) base.extend(ClassMethods) end module ClassMethods def json_create(o) collection = self.new() o["instance_vars"].each do |k,v| collection.instance_variable_set(k.to_sym, v) end collection end end def is_chef_resource!(arg) unless arg.kind_of?(Chef::Resource) raise ArgumentError, "Cannot insert a #{arg.class} into a resource collection: must be a subclass of Chef::Resource" end true end end end end chef-12.3.0/lib/chef/resource_collection/resource_set.rb0000644000004100000410000001353412520074675023314 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource' require 'chef/resource_collection/resource_collection_serialization' class Chef class ResourceCollection class ResourceSet include ResourceCollection::ResourceCollectionSerialization # Matches a multiple resource lookup specification, # e.g., "service[nginx,unicorn]" MULTIPLE_RESOURCE_MATCH = /^(.+)\[(.+?),(.+)\]$/ # Matches a single resource lookup specification, # e.g., "service[nginx]" SINGLE_RESOURCE_MATCH = /^(.+)\[(.+)\]$/ def initialize @resources_by_key = Hash.new end def keys @resources_by_key.keys end def insert_as(resource, resource_type=nil, instance_name=nil) is_chef_resource!(resource) resource_type ||= resource.resource_name instance_name ||= resource.name key = create_key(resource_type, instance_name) @resources_by_key[key] = resource end def lookup(key) case when key.kind_of?(String) lookup_by = key when key.kind_of?(Chef::Resource) lookup_by = create_key(key.resource_name, key.name) else raise ArgumentError, "Must pass a Chef::Resource or String to lookup" end res = @resources_by_key[lookup_by] unless res raise Chef::Exceptions::ResourceNotFound, "Cannot find a resource matching #{lookup_by} (did you define it first?)" end res end # Find existing resources by searching the list of existing resources. Possible # forms are: # # find(:file => "foobar") # find(:file => [ "foobar", "baz" ]) # find("file[foobar]", "file[baz]") # find("file[foobar,baz]") # # Returns the matching resource, or an Array of matching resources. # # Raises an ArgumentError if you feed it bad lookup information # Raises a Runtime Error if it can't find the resources you are looking for. def find(*args) results = Array.new args.each do |arg| case arg when Hash results << find_resource_by_hash(arg) when String results << find_resource_by_string(arg) else msg = "arguments to #{self.class.name}#find should be of the form :resource => 'name' or 'resource[name]'" raise Chef::Exceptions::InvalidResourceSpecification, msg end end flat_results = results.flatten flat_results.length == 1 ? flat_results[0] : flat_results end # @deprecated # resources is a poorly named, but we have to maintain it for back # compat. alias_method :resources, :find # Returns true if +query_object+ is a valid string for looking up a # resource, or raises InvalidResourceSpecification if not. # === Arguments # * query_object should be a string of the form # "resource_type[resource_name]", a single element Hash (e.g., :service => # "apache2"), or a Chef::Resource (this is the happy path). Other arguments # will raise an exception. # === Returns # * true returns true for all valid input. # === Raises # * Chef::Exceptions::InvalidResourceSpecification for all invalid input. def validate_lookup_spec!(query_object) case query_object when Chef::Resource true when SINGLE_RESOURCE_MATCH, MULTIPLE_RESOURCE_MATCH true when Hash true when String raise Chef::Exceptions::InvalidResourceSpecification, "The string `#{query_object}' is not valid for resource collection lookup. Correct syntax is `resource_type[resource_name]'" else raise Chef::Exceptions::InvalidResourceSpecification, "The object `#{query_object.inspect}' is not valid for resource collection lookup. " + "Use a String like `resource_type[resource_name]' or a Chef::Resource object" end end private def create_key(resource_type, instance_name) "#{resource_type}[#{instance_name}]" end def find_resource_by_hash(arg) results = Array.new arg.each do |resource_type, name_list| instance_names = name_list.kind_of?(Array) ? name_list : [ name_list ] instance_names.each do |instance_name| results << lookup(create_key(resource_type, instance_name)) end end return results end def find_resource_by_string(arg) results = Array.new case arg when MULTIPLE_RESOURCE_MATCH resource_type = $1 arg =~ /^.+\[(.+)\]$/ resource_list = $1 resource_list.split(",").each do |instance_name| results << lookup(create_key(resource_type, instance_name)) end when SINGLE_RESOURCE_MATCH resource_type = $1 name = $2 results << lookup(create_key(resource_type, name)) else raise ArgumentError, "Bad string format #{arg}, you must have a string like resource_type[name]!" end return results end end end end chef-12.3.0/lib/chef/resource_collection/stepable_iterator.rb0000644000004100000410000000530212520074675024314 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class ResourceCollection class StepableIterator def self.for_collection(new_collection) instance = new(new_collection) instance end attr_accessor :collection attr_reader :position def initialize(collection=[]) @position = 0 @paused = false @collection = collection end def size collection.size end def each(&block) reset_iteration(block) @iterator_type = :element iterate end def each_index(&block) reset_iteration(block) @iterator_type = :index iterate end def each_with_index(&block) reset_iteration(block) @iterator_type = :element_with_index iterate end def paused? @paused end def pause @paused = true end def resume @paused = false iterate end def rewind @position = 0 end def skip_back(skips=1) @position -= skips end def skip_forward(skips=1) @position += skips end def step return nil if @position == size call_iterator_block @position += 1 end def iterate_on(iteration_type, &block) @iterator_type = iteration_type @iterator_block = block end private def reset_iteration(iterator_block) @iterator_block = iterator_block @position = 0 @paused = false end def iterate while @position < size && !paused? step end collection end def call_iterator_block case @iterator_type when :element @iterator_block.call(collection[@position]) when :index @iterator_block.call(@position) when :element_with_index @iterator_block.call(collection[@position], @position) else raise "42error: someone forgot to set @iterator_type, wtf?" end end end end end chef-12.3.0/lib/chef/resource_collection/resource_list.rb0000644000004100000410000000745012520074675023474 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource' require 'chef/resource_collection/stepable_iterator' require 'chef/resource_collection/resource_collection_serialization' require 'forwardable' # This class keeps the list of all known Resources in the order they are to be executed in. It also keeps a pointer # to the most recently executed resource so we can add resources-to-execute after this point. class Chef class ResourceCollection class ResourceList include ResourceCollection::ResourceCollectionSerialization include Enumerable extend Forwardable attr_reader :iterator attr_reader :resources private :resources # Delegate direct access methods to the @resources array # 4 extra methods here are not included in the Enumerable's instance methods direct_access_methods = Enumerable.instance_methods + [ :[], :each, :each_index, :empty? ] def_delegators :resources, *(direct_access_methods) def initialize @resources = Array.new @insert_after_idx = nil end # @param resource [Chef::Resource] The resource to insert # If @insert_after_idx is nil, we are not currently executing a converge so the Resource is appended to the # end of the list. If @insert_after_idx is NOT nil, we ARE currently executing a converge so the resource # is inserted into the middle of the list after the last resource that was converged. If it is called multiple # times (when an LWRP contains multiple resources) it keeps track of that. See this example ResourceList: # [File1, LWRP1, File2] # The iterator starts and points to File1. It is executed and @insert_after_idx=0 # [File1, LWRP1, File2] # The iterator moves to LWRP1. It is executed and @insert_after_idx=1 # [File1, LWRP1, Service1, File2] # The LWRP execution inserts Service1 and @insert_after_idx=2 # [File1, LWRP1, Service1, Service2, File2] # The LWRP inserts Service2 and @insert_after_idx=3. The LWRP # finishes executing # [File1, LWRP1, Service1, Service2, File2] # The iterator moves to Service1 since it is the next non-executed # resource. The execute_each_resource call below resets @insert_after_idx=2 # If Service1 was another LWRP, it would insert its resources between Service1 and Service2. The iterator keeps # track of executed resources and @insert_after_idx keeps track of where the next resource to insert should be. def insert(resource) is_chef_resource!(resource) if @insert_after_idx @resources.insert(@insert_after_idx += 1, resource) else @resources << resource end end # @deprecated - can be removed when it is removed from resource_collection.rb def []=(index, resource) @resources[index] = resource end def all_resources @resources end def execute_each_resource(&resource_exec_block) @iterator = ResourceCollection::StepableIterator.for_collection(@resources) @iterator.each_with_index do |resource, idx| @insert_after_idx = idx yield resource end end end end end chef-12.3.0/lib/chef/guard_interpreter/0000755000004100000410000000000012520074675017742 5ustar www-datawww-datachef-12.3.0/lib/chef/guard_interpreter/resource_guard_interpreter.rb0000644000004100000410000001166612520074675025735 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 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/guard_interpreter' class Chef class GuardInterpreter class ResourceGuardInterpreter < DefaultGuardInterpreter def initialize(parent_resource, command, opts, &block) super(command, opts) @parent_resource = parent_resource @resource = get_interpreter_resource(parent_resource) end def evaluate # Add attributes inherited from the parent class # to the resource merge_inherited_attributes # Only execute and script resources and use guard attributes. # The command to be executed on them are passed via different attributes. # Script resources use code attribute and execute resources use # command attribute. Moreover script resources are also execute # resources. Here we make sure @command is assigned to the right # attribute by checking the type of the resources. # We need to make sure we check for Script first because any resource # that can get to here is an Execute resource. if @resource.is_a? Chef::Resource::Script block_attributes = @command_opts.merge({:code => @command}) else block_attributes = @command_opts.merge({:command => @command}) end # Handles cases like powershell_script where default # attributes are different when used in a guard vs. not. For # powershell_script in particular, this will go away when # the one attribue that causes this changes its default to be # the same after some period to prepare for deprecation if @resource.class.respond_to?(:get_default_attributes) block_attributes = @resource.class.send(:get_default_attributes, @command_opts).merge(block_attributes) end resource_block = block_from_attributes(block_attributes) evaluate_action(nil, &resource_block) end protected def evaluate_action(action=nil, &block) @resource.instance_eval(&block) run_action = action || @resource.action begin @resource.run_action(run_action) resource_updated = @resource.updated rescue Mixlib::ShellOut::ShellCommandFailed resource_updated = nil end resource_updated end def get_interpreter_resource(parent_resource) if parent_resource.nil? || parent_resource.node.nil? raise ArgumentError, "Node for guard resource parent must not be nil" end resource_class = Chef::Resource.resource_for_node(parent_resource.guard_interpreter, parent_resource.node) if resource_class.nil? raise ArgumentError, "Specified guard_interpreter resource #{parent_resource.guard_interpreter.to_s} unknown for this platform" end if ! resource_class.ancestors.include?(Chef::Resource::Execute) raise ArgumentError, "Specified guard interpreter class #{resource_class} must be a kind of Chef::Resource::Execute resource" end empty_events = Chef::EventDispatch::Dispatcher.new anonymous_run_context = Chef::RunContext.new(parent_resource.node, {}, empty_events) interpreter_resource = resource_class.new('Guard resource', anonymous_run_context) interpreter_resource.is_guard_interpreter = true interpreter_resource end def block_from_attributes(attributes) Proc.new do attributes.keys.each do |attribute_name| send(attribute_name, attributes[attribute_name]) if respond_to?(attribute_name) end end end def merge_inherited_attributes inherited_attributes = [] if @parent_resource.class.respond_to?(:guard_inherited_attributes) inherited_attributes = @parent_resource.class.send(:guard_inherited_attributes) end if inherited_attributes && !inherited_attributes.empty? inherited_attributes.each do |attribute| if @parent_resource.respond_to?(attribute) && @resource.respond_to?(attribute) parent_value = @parent_resource.send(attribute) child_value = @resource.send(attribute) if parent_value || child_value @resource.send(attribute, parent_value) end end end end end end end end chef-12.3.0/lib/chef/guard_interpreter/default_guard_interpreter.rb0000644000004100000410000000221212520074675025515 0ustar www-datawww-data# # Author:: Adam Edwards () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class GuardInterpreter class DefaultGuardInterpreter include Chef::Mixin::ShellOut protected def initialize(command, opts) @command = command @command_opts = opts end public def evaluate shell_out(@command, @command_opts).status.success? rescue Chef::Exceptions::CommandTimeout Chef::Log.warn "Command '#{@command}' timed out" false end end end end chef-12.3.0/lib/chef/application/0000755000004100000410000000000012520074675016520 5ustar www-datawww-datachef-12.3.0/lib/chef/application/windows_service_manager.rb0000644000004100000410000001602612520074675023756 0ustar www-datawww-data# # Author:: Seth Chisamore () # 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. # if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'win32/service' end require 'chef/config' require 'mixlib/cli' class Chef class Application # # This class is used to create and manage a windows service. # Service should be created using Daemon class from # win32/service gem. # For an example see: Chef::Application::WindowsService # # Outside programs are expected to use this class to manage # windows services. # class WindowsServiceManager include Mixlib::CLI option :action, :short => "-a ACTION", :long => "--action ACTION", :default => "status", :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 :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true, :show_options => true, :exit => 0 option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 def initialize(service_options) # having to call super in initialize is the most annoying # anti-pattern :( super() raise ArgumentError, "Service definition is not provided" if service_options.nil? required_options = [:service_name, :service_display_name, :service_name, :service_description, :service_file_path] required_options.each do |req_option| if !service_options.has_key?(req_option) raise ArgumentError, "Service definition doesn't contain required option #{req_option}" end end @service_name = service_options[:service_name] @service_display_name = service_options[:service_display_name] @service_description = service_options[:service_description] @service_file_path = service_options[:service_file_path] @service_start_name = service_options[:run_as_user] @password = service_options[:run_as_password] end def run(params = ARGV) parse_options(params) case config[:action] when 'install' if service_exists? puts "Service #{@service_name} already exists on the system." else ruby = File.join(RbConfig::CONFIG['bindir'], 'ruby') opts = "" opts << " -c #{config[:config_file]}" if config[:config_file] opts << " -L #{config[:log_location]}" if config[:log_location] # Quote the full paths to deal with possible spaces in the path name. # Also ensure all forward slashes are backslashes cmd = "\"#{ruby}\" \"#{@service_file_path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR) ::Win32::Service.new( :service_name => @service_name, :display_name => @service_display_name, :description => @service_description, # Prior to 0.8.5, win32-service creates interactive services by default, # and we don't want that, so we need to override the service type. :service_type => ::Win32::Service::SERVICE_WIN32_OWN_PROCESS, :start_type => ::Win32::Service::SERVICE_AUTO_START, :binary_path_name => cmd, :service_start_name => @service_start_name, :password => @password, ) puts "Service '#{@service_name}' has successfully been installed." end when 'status' if !service_exists? puts "Service #{@service_name} doesn't exist on the system." else puts "State of #{@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 #{@service_name} doesn't exist on the system." else ::Win32::Service.delete(@service_name) puts "Service #{@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?(@service_name) end def take_action(action=nil, desired_state=nil) if service_exists? if current_state != desired_state ::Win32::Service.send(action, @service_name) wait_for_state(desired_state) puts "Service '#{@service_name}' is now '#{current_state}'." else puts "Service '#{@service_name}' is already '#{desired_state}'." end else puts "Cannot '#{action}' service '#{@service_name}'" puts "Service #{@service_name} doesn't exist on the system." end end def current_state ::Win32::Service.status(@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 chef-12.3.0/lib/chef/application/client.rb0000644000004100000410000003715112520074675020332 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Mark Mzyk (mmzyk@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 'chef/application' require 'chef/client' require 'chef/config' require 'chef/daemon' require 'chef/log' require 'chef/config_fetcher' require 'chef/handler/error_report' require 'chef/workstation_config_loader' class Chef::Application::Client < Chef::Application include Chef::Mixin::ShellOut # Mimic self_pipe sleep from Unicorn to capture signals safely SELF_PIPE = [] option :config_file, :short => "-c CONFIG", :long => "--config CONFIG", :description => "The configuration file to use" option :formatter, :short => "-F FORMATTER", :long => "--format FORMATTER", :description => "output format to use", :proc => lambda { |format| Chef::Config.add_formatter(format) } option :force_logger, :long => "--force-logger", :description => "Use logger output instead of formatter output", :boolean => true, :default => false option :force_formatter, :long => "--force-formatter", :description => "Use formatter output instead of logger output", :boolean => true, :default => false option :color, :long => '--[no-]color', :boolean => true, :default => !Chef::Platform.windows?, :description => "Use colored output, defaults to false on Windows, true otherwise" option :log_level, :short => "-l LEVEL", :long => "--log_level LEVEL", :description => "Set the log level (auto, debug, info, warn, error, fatal)", :proc => lambda { |l| l.to_sym } option :log_location, :short => "-L LOGLOCATION", :long => "--logfile LOGLOCATION", :description => "Set the log file location, defaults to STDOUT - recommended for daemonizing", :proc => nil option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true, :show_options => true, :exit => 0 option :user, :short => "-u USER", :long => "--user USER", :description => "User to set privilege to", :proc => nil option :group, :short => "-g GROUP", :long => "--group GROUP", :description => "Group to set privilege to", :proc => nil unless Chef::Platform.windows? option :daemonize, :short => "-d", :long => "--daemonize", :description => "Daemonize the process", :proc => lambda { |p| true } end option :pid_file, :short => "-P PID_FILE", :long => "--pid PIDFILE", :description => "Set the PID file location, for the chef-client daemon process. Defaults to /tmp/chef-client.pid", :proc => nil option :lockfile, :long => "--lockfile LOCKFILE", :description => "Set the lockfile location. Prevents multiple client processes from converging at the same time", :proc => nil option :interval, :short => "-i SECONDS", :long => "--interval SECONDS", :description => "Run chef-client periodically, in seconds", :proc => lambda { |s| s.to_i } option :once, :long => "--once", :description => "Cancel any interval or splay options, run chef once and exit", :boolean => true option :json_attribs, :short => "-j JSON_ATTRIBS", :long => "--json-attributes JSON_ATTRIBS", :description => "Load attributes from a JSON file or URL", :proc => nil option :node_name, :short => "-N NODE_NAME", :long => "--node-name NODE_NAME", :description => "The node name for this client", :proc => nil 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 :chef_server_url, :short => "-S CHEFSERVERURL", :long => "--server CHEFSERVERURL", :description => "The chef server URL", :proc => nil option :validation_key, :short => "-K KEY_FILE", :long => "--validation_key KEY_FILE", :description => "Set the validation key file location, used for registering new clients", :proc => nil option :client_key, :short => "-k KEY_FILE", :long => "--client_key KEY_FILE", :description => "Set the client key file location", :proc => nil option :environment, :short => '-E ENVIRONMENT', :long => '--environment ENVIRONMENT', :description => 'Set the Chef Environment on the node' option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 option :override_runlist, :short => "-o RunlistItem,RunlistItem...", :long => "--override-runlist RunlistItem,RunlistItem...", :description => "Replace current run list with specified items for a single run", :proc => lambda{|items| items = items.split(',') items.compact.map{|item| Chef::RunList::RunListItem.new(item) } } option :runlist, :short => "-r RunlistItem,RunlistItem...", :long => "--runlist RunlistItem,RunlistItem...", :description => "Permanently replace current run list with specified items", :proc => lambda{|items| items = items.split(',') items.compact.map{|item| Chef::RunList::RunListItem.new(item) } } option :why_run, :short => '-W', :long => '--why-run', :description => 'Enable whyrun mode', :boolean => true option :client_fork, :short => "-f", :long => "--[no-]fork", :description => "Fork client", :boolean => true option :recipe_url, :long => "--recipe-url=RECIPE_URL", :description => "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode." option :enable_reporting, :short => "-R", :long => "--enable-reporting", :description => "Enable reporting data collection for chef runs", :boolean => true option :local_mode, :short => "-z", :long => "--local-mode", :description => "Point chef-client at local repository", :boolean => true option :chef_zero_host, :long => "--chef-zero-host HOST", :description => "Host to start chef-zero on" option :chef_zero_port, :long => "--chef-zero-port PORT", :description => "Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works." option :disable_config, :long => "--disable-config", :description => "Refuse to load a config file and use defaults. This is for development and not a stable API", :boolean => true option :run_lock_timeout, :long => "--run-lock-timeout SECONDS", :description => "Set maximum duration to wait for another client run to finish, default is indefinitely.", :proc => lambda { |s| s.to_i } if Chef::Platform.windows? option :fatal_windows_admin_check, :short => "-A", :long => "--fatal-windows-admin-check", :description => "Fail the run when chef-client doesn't have administrator privileges on Windows", :boolean => true end option :audit_mode, :long => "--audit-mode MODE", :description => "Enable audit-mode with `enabled`. Disable audit-mode with `disabled`. Skip converge and only perform audits with `audit-only`", :proc => lambda { |mo| mo.gsub("-", "_").to_sym } option :minimal_ohai, :long => "--minimal-ohai", :description => "Only run the bare minimum ohai plugins chef needs to function", :boolean => true option :listen, :long => "--[no-]listen", :description => "Whether a local mode (-z) server binds to a port", :boolean => true IMMEDIATE_RUN_SIGNAL = "1".freeze attr_reader :chef_client_json # Reconfigure the chef client # Re-open the JSON attributes and load them into the node def reconfigure super raise Chef::Exceptions::PIDFileLockfileMatch if Chef::Util::PathHelper.paths_eql? (Chef::Config[:pid_file] || '' ), (Chef::Config[:lockfile] || '') set_specific_recipes Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode) if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path) Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd) end if !Chef::Config.local_mode && Chef::Config.has_key?(:recipe_url) Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode", 1) elsif Chef::Config.local_mode && Chef::Config.has_key?(:recipe_url) Chef::Log.debug "Creating path #{Chef::Config.chef_repo_path} to extract recipes into" FileUtils.mkdir_p(Chef::Config.chef_repo_path) tarball_path = File.join(Chef::Config.chef_repo_path, 'recipes.tgz') fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}") Chef::Log.debug "#{result.stdout}" end Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host] Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port] if Chef::Config[:daemonize] Chef::Config[:interval] ||= 1800 end if Chef::Config[:once] Chef::Config[:interval] = nil Chef::Config[:splay] = nil end if !Chef::Config[:client_fork] && Chef::Config[:interval] && !Chef::Platform.windows? Chef::Application.fatal!(unforked_interval_error_message) end if Chef::Config[:json_attribs] config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @chef_client_json = config_fetcher.fetch_json end if mode = config[:audit_mode] || Chef::Config[:audit_mode] expected_modes = [:enabled, :disabled, :audit_only] unless expected_modes.include?(mode) Chef::Application.fatal!(unrecognized_audit_mode(mode)) end unless mode == :disabled # This should be removed when audit-mode is enabled by default/no longer # an experimental feature. Chef::Log.warn(audit_mode_experimental_message) end end end def load_config_file if !config.has_key?(:config_file) && !config[:disable_config] if config[:local_mode] config[:config_file] = Chef::WorkstationConfigLoader.new(nil, Chef::Log).config_location else config[:config_file] = Chef::Config.platform_specific_path("/etc/chef/client.rb") end end super end def configure_logging super Mixlib::Authentication::Log.use_log_devices( Chef::Log ) Ohai::Log.use_log_devices( Chef::Log ) end def setup_application Chef::Daemon.change_privilege end def setup_signal_handlers super unless Chef::Platform.windows? SELF_PIPE.replace IO.pipe trap("USR1") do Chef::Log.info("SIGUSR1 received, waking up") SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select end end end # Run the chef client, optionally daemonizing or looping at intervals. def run_application if Chef::Config[:version] puts "Chef version: #{::Chef::VERSION}" end if !Chef::Config[:client_fork] || Chef::Config[:once] begin # run immediately without interval sleep, or splay run_chef_client(Chef::Config[:specific_recipes]) rescue SystemExit raise rescue Exception => e Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end else interval_run_chef_client end end private def interval_run_chef_client if Chef::Config[:daemonize] Chef::Daemon.daemonize("chef-client") end loop do begin @signal = test_signal if @signal != IMMEDIATE_RUN_SIGNAL sleep_sec = time_to_sleep Chef::Log.debug("Sleeping for #{sleep_sec} seconds") interval_sleep(sleep_sec) end @signal = nil run_chef_client(Chef::Config[:specific_recipes]) Chef::Application.exit!("Exiting", 0) if !Chef::Config[:interval] rescue SystemExit => e raise rescue Exception => e if Chef::Config[:interval] Chef::Log.error("#{e.class}: #{e}") Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}") retry else Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end end end end def test_signal @signal = interval_sleep(0) end def time_to_sleep duration = 0 duration += rand(Chef::Config[:splay]) if Chef::Config[:splay] duration += Chef::Config[:interval] if Chef::Config[:interval] duration end def interval_sleep(sec) unless SELF_PIPE.empty? client_sleep(sec) else # Windows sleep(sec) end end def client_sleep(sec) IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return @signal = SELF_PIPE[0].getc.chr end def unforked_interval_error_message "Unforked chef-client interval runs are disabled in Chef 12." + "\nConfiguration settings:" + "#{"\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]}" + "\nEnable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options." end def audit_mode_settings_explaination "\n* To enable audit mode after converge, use command line option `--audit-mode enabled` or set `:audit_mode = :enabled` in your config file." + "\n* To disable audit mode, use command line option `--audit-mode disabled` or set `:audit_mode = :disabled` in your config file." + "\n* To only run audit mode, use command line option `--audit-mode audit-only` or set `:audit_mode = :audit_only` in your config file." + "\nAudit mode is disabled by default." end def unrecognized_audit_mode(mode) "Unrecognized setting #{mode} for audit mode." + audit_mode_settings_explaination end def audit_mode_experimental_message msg = if Chef::Config[:audit_mode] == :audit_only "Chef-client has been configured to skip converge and only audit." else "Chef-client has been configured to audit after it converges." end msg += " Audit mode is an experimental feature currently under development. API changes may occur. Use at your own risk." msg += audit_mode_settings_explaination return msg end def fetch_recipe_tarball(url, path) Chef::Log.debug("Download recipes tarball from #{url} to #{path}") File.open(path, 'wb') do |f| open(url) do |r| f.write(r.read) end end end end chef-12.3.0/lib/chef/application/knife.rb0000644000004100000410000001264212520074675020146 0ustar www-datawww-data# # Author:: Adam Jacob ( "-c CONFIG", :long => "--config CONFIG", :description => "The configuration file to use", :proc => lambda { |path| File.expand_path(path, Dir.pwd) } verbosity_level = 0 option :verbosity, :short => '-V', :long => '--verbose', :description => "More verbose output. Use twice for max verbosity", :proc => Proc.new { verbosity_level += 1}, :default => 0 option :color, :long => '--[no-]color', :boolean => true, :default => !Chef::Platform.windows?, :description => "Use colored output, defaults to false on Windows, true otherwise" option :environment, :short => "-E ENVIRONMENT", :long => "--environment ENVIRONMENT", :description => "Set the Chef environment (except for in searches, where this will be flagrantly ignored)" option :editor, :short => "-e EDITOR", :long => "--editor EDITOR", :description => "Set the editor to use for interactive commands", :default => ENV['EDITOR'] option :disable_editing, :short => "-d", :long => "--disable-editing", :description => "Do not open EDITOR, just accept the data as is", :boolean => true, :default => false option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true option :node_name, :short => "-u USER", :long => "--user USER", :description => "API Client Username" option :client_key, :short => "-k KEY", :long => "--key KEY", :description => "API Client Key", :proc => lambda { |path| File.expand_path(path, Dir.pwd) } option :chef_server_url, :short => "-s URL", :long => "--server-url URL", :description => "Chef Server URL" option :yes, :short => "-y", :long => "--yes", :description => "Say yes to all prompts for confirmation" option :defaults, :long => "--defaults", :description => "Accept default values for all questions" option :print_after, :long => "--print-after", :description => "Show the data after a destructive operation" option :format, :short => "-F FORMAT", :long => "--format FORMAT", :description => "Which format to use for output", :default => "summary" option :local_mode, :short => "-z", :long => "--local-mode", :description => "Point knife commands at local repository instead of server", :boolean => true option :chef_zero_host, :long => "--chef-zero-host HOST", :description => "Host to start chef-zero on" option :chef_zero_port, :long => "--chef-zero-port PORT", :description => "Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works." option :listen, :long => "--[no-]listen", :description => "Whether a local mode (-z) server binds to a port", :boolean => true option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 # Run knife def run Mixlib::Log::Formatter.show_time = false validate_and_parse_options quiet_traps Chef::Knife.run(ARGV, options) exit 0 end private def quiet_traps trap("TERM") do exit 1 end trap("INT") do exit 2 end end def validate_and_parse_options # Checking ARGV validity *before* parse_options because parse_options # mangles ARGV in some situations if no_command_given? print_help_and_exit(1, NO_COMMAND_GIVEN) elsif no_subcommand_given? if (want_help? || want_version?) print_help_and_exit else print_help_and_exit(2, NO_COMMAND_GIVEN) end end end def no_subcommand_given? ARGV[0] =~ /^-/ end def no_command_given? ARGV.empty? end def want_help? ARGV[0] =~ /^(--help|-h)$/ end def want_version? ARGV[0] =~ /^(--version|-v)$/ end def print_help_and_exit(exitcode=1, fatal_message=nil) Chef::Log.error(fatal_message) if fatal_message begin self.parse_options rescue OptionParser::InvalidOption => e puts "#{e}\n" end puts self.opt_parser puts Chef::Knife.list_commands exit exitcode end end chef-12.3.0/lib/chef/application/solo.rb0000644000004100000410000002245612520074675020032 0ustar www-datawww-data# # Author:: AJ Christensen () # Author:: Mark Mzyk (mmzyk@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 'chef' require 'chef/application' require 'chef/client' require 'chef/config' require 'chef/daemon' require 'chef/log' require 'chef/rest' require 'chef/config_fetcher' require 'fileutils' class Chef::Application::Solo < Chef::Application option :config_file, :short => "-c CONFIG", :long => "--config CONFIG", :default => Chef::Config.platform_specific_path('/etc/chef/solo.rb'), :description => "The configuration file to use" option :formatter, :short => "-F FORMATTER", :long => "--format FORMATTER", :description => "output format to use", :proc => lambda { |format| Chef::Config.add_formatter(format) } option :force_logger, :long => "--force-logger", :description => "Use logger output instead of formatter output", :boolean => true, :default => false option :force_formatter, :long => "--force-formatter", :description => "Use formatter output instead of logger output", :boolean => true, :default => false option :color, :long => '--[no-]color', :boolean => true, :default => !Chef::Platform.windows?, :description => "Use colored output, defaults to enabled" option :log_level, :short => "-l LEVEL", :long => "--log_level LEVEL", :description => "Set the log level (debug, info, warn, error, fatal)", :proc => lambda { |l| l.to_sym } option :log_location, :short => "-L LOGLOCATION", :long => "--logfile LOGLOCATION", :description => "Set the log file location, defaults to STDOUT", :proc => nil option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true, :show_options => true, :exit => 0 option :user, :short => "-u USER", :long => "--user USER", :description => "User to set privilege to", :proc => nil option :group, :short => "-g GROUP", :long => "--group GROUP", :description => "Group to set privilege to", :proc => nil unless Chef::Platform.windows? option :daemonize, :short => "-d", :long => "--daemonize", :description => "Daemonize the process", :proc => lambda { |p| true } end option :lockfile, :long => "--lockfile LOCKFILE", :description => "Set the lockfile location. Prevents multiple processes from converging at the same time", :proc => nil option :interval, :short => "-i SECONDS", :long => "--interval SECONDS", :description => "Run chef-client periodically, in seconds", :proc => lambda { |s| s.to_i } option :json_attribs, :short => "-j JSON_ATTRIBS", :long => "--json-attributes JSON_ATTRIBS", :description => "Load attributes from a JSON file or URL", :proc => nil option :node_name, :short => "-N NODE_NAME", :long => "--node-name NODE_NAME", :description => "The node name for this client", :proc => nil 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 :recipe_url, :short => "-r RECIPE_URL", :long => "--recipe-url RECIPE_URL", :description => "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache.", :proc => nil option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 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) } } option :client_fork, :short => "-f", :long => "--[no-]fork", :description => "Fork client", :boolean => true option :why_run, :short => '-W', :long => '--why-run', :description => 'Enable whyrun mode', :boolean => true option :ez, :long => '--ez', :description => 'A memorial for Ezra Zygmuntowicz', :boolean => true option :environment, :short => '-E ENVIRONMENT', :long => '--environment ENVIRONMENT', :description => 'Set the Chef Environment on the node' option :run_lock_timeout, :long => "--run-lock-timeout SECONDS", :description => "Set maximum duration to wait for another client run to finish, default is indefinitely.", :proc => lambda { |s| s.to_i } option :minimal_ohai, :long => "--minimal-ohai", :description => "Only run the bare minimum ohai plugins chef needs to function", :boolean => true attr_reader :chef_client_json def initialize super end def reconfigure super set_specific_recipes Chef::Config[:solo] = true if Chef::Config[:daemonize] Chef::Config[:interval] ||= 1800 end Chef::Application.fatal!(unforked_interval_error_message) if !Chef::Config[:client_fork] && Chef::Config[:interval] if Chef::Config[:recipe_url] cookbooks_path = Array(Chef::Config[:cookbook_path]).detect{|e| e =~ /\/cookbooks\/*$/ } recipes_path = File.expand_path(File.join(cookbooks_path, '..')) Chef::Log.debug "Cleanup path #{recipes_path} before extract recipes into it" FileUtils.rm_rf(recipes_path, :secure => true) Chef::Log.debug "Creating path #{recipes_path} to extract recipes into" FileUtils.mkdir_p(recipes_path) tarball_path = File.join(recipes_path, 'recipes.tgz') fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) Chef::Mixin::Command.run_command(:command => "tar zxvf #{tarball_path} -C #{recipes_path}") end # json_attribs shuld be fetched after recipe_url tarball is unpacked. # Otherwise it may fail if points to local file from tarball. if Chef::Config[:json_attribs] config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @chef_client_json = config_fetcher.fetch_json end # Disable auditing for solo Chef::Config[:audit_mode] = :disabled end def setup_application Chef::Daemon.change_privilege end def run_application for_ezra if Chef::Config[:ez] if !Chef::Config[:client_fork] || Chef::Config[:once] # Run immediately without interval sleep or splay begin run_chef_client(Chef::Config[:specific_recipes]) rescue SystemExit raise rescue Exception => e Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end else interval_run_chef_client end end private def for_ezra puts <<-EOH For Ezra Zygmuntowicz: The man who brought you Chef Solo Early contributor to Chef Kind hearted open source advocate Rest in peace, Ezra. EOH end def interval_run_chef_client if Chef::Config[:daemonize] Chef::Daemon.daemonize("chef-client") end loop do begin sleep_sec = 0 sleep_sec += rand(Chef::Config[:splay]) if Chef::Config[:splay] sleep_sec += Chef::Config[:interval] if Chef::Config[:interval] if sleep_sec != 0 Chef::Log.debug("Sleeping for #{sleep_sec} seconds") sleep(sleep_sec) end run_chef_client if !Chef::Config[:interval] Chef::Application.exit! "Exiting", 0 end rescue SystemExit => e raise rescue Exception => e if Chef::Config[:interval] Chef::Log.error("#{e.class}: #{e}") Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}") retry else Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end end end end def fetch_recipe_tarball(url, path) Chef::Log.debug("Download recipes tarball from #{url} to #{path}") File.open(path, 'wb') do |f| open(url) do |r| f.write(r.read) end end end def unforked_interval_error_message "Unforked chef-client interval runs are disabled in Chef 12." + "\nConfiguration settings:" + "#{"\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]}" + "\nEnable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options." end end chef-12.3.0/lib/chef/application/windows_service.rb0000644000004100000410000003012112520074675022254 0ustar www-datawww-data# # Author:: Christopher Maier () # 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 'chef' require 'chef/monologger' require 'chef/application' require 'chef/client' require 'chef/config' require 'chef/handler/error_report' require 'chef/log' require 'chef/rest' require 'mixlib/cli' require 'socket' require 'uri' require 'win32/daemon' require 'chef/mixin/shell_out' class Chef class Application class WindowsService < ::Win32::Daemon include Mixlib::CLI include Chef::Mixin::ShellOut option :config_file, :short => "-c CONFIG", :long => "--config CONFIG", :default => "#{ENV['SYSTEMDRIVE']}/chef/client.rb", :description => "" option :log_location, :short => "-L LOGLOCATION", :long => "--logfile LOGLOCATION", :description => "Set the log file location", :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 } 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? 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] # Honor splay sleep config timeout += rand Chef::Config[:splay] # 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 rescue SystemExit => e # 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}") end end end # Daemon class needs to have all the signal callbacks return # before service_main returns. Chef::Log.debug("Giving signal callbacks some time to exit...") sleep 1 Chef::Log.debug("Exiting service...") end ################################################################################ # Control Signal Callback Methods ################################################################################ def service_stop run_warning_displayed = false Chef::Log.info("STOP request from operating system.") loop do # See if a run is in flight if @service_action_mutex.try_lock # Run is not in flight. Wake up service_main to exit. @service_signal.signal @service_action_mutex.unlock break else unless run_warning_displayed Chef::Log.info("Currently a chef run is happening on this system.") Chef::Log.info("Service will stop when run is completed.") run_warning_displayed = true end Chef::Log.debug("Waiting for chef-client run...") sleep 1 end end Chef::Log.info("Service is stopping....") end def service_pause 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 # 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("SHUTDOWN signal received from the OS.") # Treat shutdown similar to stop. service_stop end ################################################################################ # Internal Methods ################################################################################ private # Initializes Chef::Client instance and runs it def run_chef_client # The chef client will be started in a new process. We have used shell_out to start the chef-client. # The log_location and config_file of the parent process is passed to the new chef-client process. # We need to add the --no-fork, as by default it is set to fork=true. begin Chef::Log.info "Starting chef-client in a new process" # Pass config params to the new process config_params = " --no-fork" config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil? config_params += " -L #{Chef::Config[:log_location]}" unless Chef::Config[:log_location] == STDOUT # Starts a new process and waits till the process exits result = shell_out("chef-client #{config_params}", :timeout => Chef::Config[:windows_service][:watchdog_timeout]) Chef::Log.debug "#{result.stdout}" Chef::Log.debug "#{result.stderr}" rescue Mixlib::ShellOut::CommandTimeout => e Chef::Log.error "chef-client timed out\n(#{e})" Chef::Log.error(<<-EOF) Your chef-client run timed out. You can increase the time chef-client is given to complete by configuring windows_service.watchdog_timeout in your client.rb. EOF rescue Mixlib::ShellOut::ShellCommandFailed => e Chef::Log.warn "Not able to start chef-client in new process (#{e})" rescue => e Chef::Log.error e ensure # Once process exits, we log the current process' pid Chef::Log.info "Child process exited (pid: #{Process.pid})" end end def apply_config(config_file_path) Chef::Config.from_file(config_file_path) Chef::Config.merge!(config) end # Lifted from Chef::Application, with addition of optional startup parameters # for playing nicely with Windows Services def reconfigure(startup_parameters=[]) configure_chef startup_parameters configure_logging Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url unless Chef::Config[:exception_handlers].any? {|h| Chef::Handler::ErrorReport === h} Chef::Config[:exception_handlers] << Chef::Handler::ErrorReport.new end Chef::Config[:interval] ||= 1800 end # Lifted from application.rb # See application.rb for related comments. def configure_logging Chef::Log.init(MonoLogger.new(Chef::Config[:log_location])) if want_additional_logger? configure_stdout_logger end Chef::Log.level = resolve_log_level end def configure_stdout_logger stdout_logger = MonoLogger.new(STDOUT) 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) # Bit of a hack ahead: # It is possible to specify a service's binary_path_name with arguments, like "foo.exe -x argX". # It is also possible to specify startup parameters separately, either via the Services manager # or by using the registry (I think). # In order to accommodate all possible sources of parameterization, we first parse any command line # arguments. We then parse any startup parameters. This works, because Mixlib::CLI reuses its internal # 'config' hash; thus, anything in startup parameters will override any command line parameters that # might be set via the service's binary_path_name # # All these parameters then get layered on top of those from Chef::Config parse_options # Operates on ARGV by default parse_options startup_parameters begin case config[:config_file] when /^(http|https):\/\// Chef::REST.new("", nil, nil).fetch(config[:config_file]) { |f| apply_config(f.path) } else ::File::open(config[:config_file]) { |f| apply_config(f.path) } end rescue Errno::ENOENT => error Chef::Log.warn("*****************************************") Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.") Chef::Log.warn("*****************************************") Chef::Config.merge!(config) rescue SocketError => error Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", 2) rescue Chef::Exceptions::ConfigurationError => error Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2) rescue Exception => error Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2) end end end end end # To run this file as a service, it must be called as a script from within # the Windows Service framework. In that case, kick off the main loop! if __FILE__ == $0 Chef::Application::WindowsService.mainloop end chef-12.3.0/lib/chef/application/apply.rb0000644000004100000410000001262712520074675020202 0ustar www-datawww-data# # Author:: Bryan W. Berry () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2012 Bryan W. Berry # Copyright:: Copyright (c) 2012 Daniel DeLeo # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'chef' require 'chef/application' require 'chef/client' require 'chef/config' require 'chef/log' require 'fileutils' require 'tempfile' require 'chef/providers' require 'chef/resources' class Chef::Application::Apply < Chef::Application banner "Usage: chef-apply [RECIPE_FILE] [-e RECIPE_TEXT] [-s]" option :execute, :short => "-e RECIPE_TEXT", :long => "--execute RECIPE_TEXT", :description => "Execute resources supplied in a string", :proc => nil option :stdin, :short => "-s", :long => "--stdin", :description => "Execute resources read from STDIN", :boolean => true option :json_attribs, :short => "-j JSON_ATTRIBS", :long => "--json-attributes JSON_ATTRIBS", :description => "Load attributes from a JSON file or URL", :proc => nil option :log_level, :short => "-l LEVEL", :long => "--log_level LEVEL", :description => "Set the log level (debug, info, warn, error, fatal)", :proc => lambda { |l| l.to_sym } option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true, :show_options => true, :exit => 0 option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 option :why_run, :short => '-W', :long => '--why-run', :description => 'Enable whyrun mode', :boolean => true option :color, :long => '--[no-]color', :boolean => true, :default => !Chef::Platform.windows?, :description => "Use colored output, defaults to enabled" option :minimal_ohai, :long => "--minimal-ohai", :description => "Only run the bare minimum ohai plugins chef needs to function", :boolean => true attr_reader :json_attribs def initialize super end def reconfigure parse_options Chef::Config.merge!(config) configure_logging configure_proxy_environment_variables parse_json end def parse_json if Chef::Config[:json_attribs] config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @json_attribs = config_fetcher.fetch_json end end def read_recipe_file(file_name) if file_name.nil? Chef::Application.fatal!("No recipe file was provided", 1) else recipe_path = File.expand_path(file_name) unless File.exist?(recipe_path) Chef::Application.fatal!("No file exists at #{recipe_path}", 1) end recipe_fh = open(recipe_path) recipe_text = recipe_fh.read [recipe_text, recipe_fh] end end def get_recipe_and_run_context Chef::Config[:solo] = true @chef_client = Chef::Client.new(@json_attribs) @chef_client.run_ohai @chef_client.load_node @chef_client.build_node run_context = if @chef_client.events.nil? Chef::RunContext.new(@chef_client.node, {}) else Chef::RunContext.new(@chef_client.node, {}, @chef_client.events) end recipe = Chef::Recipe.new("(chef-apply cookbook)", "(chef-apply recipe)", run_context) [recipe, run_context] end # write recipe to temp file, so in case of error, # user gets error w/ context def temp_recipe_file @recipe_fh = Tempfile.open('recipe-temporary-file') @recipe_fh.write(@recipe_text) @recipe_fh.rewind @recipe_filename = @recipe_fh.path end def run_chef_recipe if config[:execute] @recipe_text = config[:execute] temp_recipe_file elsif config[:stdin] @recipe_text = STDIN.read temp_recipe_file else if !ARGV[0] puts opt_parser Chef::Application.exit! "No recipe file provided", 1 end @recipe_filename = ARGV[0] @recipe_text,@recipe_fh = read_recipe_file @recipe_filename end recipe,run_context = get_recipe_and_run_context recipe.instance_eval(@recipe_text, @recipe_filename, 1) runner = Chef::Runner.new(run_context) begin runner.converge ensure @recipe_fh.close end end def run_application begin parse_options run_chef_recipe Chef::Application.exit! "Exiting", 0 rescue SystemExit => e raise rescue Exception => e Chef::Application.debug_stacktrace(e) Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end end # Get this party started def run reconfigure run_application end end chef-12.3.0/lib/chef/monkey_patches/0000755000004100000410000000000012520074675017226 5ustar www-datawww-datachef-12.3.0/lib/chef/monkey_patches/net_http.rb0000644000004100000410000000464112520074675021405 0ustar www-datawww-data # Module gets mixed in to Net::HTTP exception classes so we can attach our # RESTRequest object to them and get the request parameters back out later. module ChefNetHTTPExceptionExtensions attr_accessor :chef_rest_request end require 'net/http' module Net class HTTPError include ChefNetHTTPExceptionExtensions end class HTTPRetriableError include ChefNetHTTPExceptionExtensions end class HTTPServerException include ChefNetHTTPExceptionExtensions end class HTTPFatalError include ChefNetHTTPExceptionExtensions end end if Net::HTTP.instance_methods.map {|m| m.to_s}.include?("proxy_uri") begin # Ruby 2.0 changes the way proxy support is implemented in Net::HTTP. # The implementation does not work correctly with IPv6 literals because it # concatenates the address into a URI without adding square brackets for # IPv6 addresses. # # If the bug is present, a call to Net::HTTP#proxy_uri when the host is an # IPv6 address will fail by creating an invalid URI, like so: # # ruby -r'net/http' -e 'Net::HTTP.new("::1", 80).proxy_uri' # /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/generic.rb:214:in `initialize': the scheme http does not accept registry part: ::1:80 (or bad hostname?) (URI::InvalidURIError) # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/http.rb:84:in `initialize' # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `new' # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:214:in `parse' # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:747:in `parse' # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/uri/common.rb:994:in `URI' # from /Users/ddeleo/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/net/http.rb:1027:in `proxy_uri' # from -e:1:in `
' # # https://bugs.ruby-lang.org/issues/9129 # # NOTE: This should be fixed in Ruby 2.2.0, and backported to Ruby 2.0 and # 2.1 (not yet released so the version/patchlevel required isn't known # yet). Net::HTTP.new("::1", 80).proxy_uri rescue URI::InvalidURIError class Net::HTTP def proxy_uri # :nodoc: ipv6_safe_addr = address.to_s.include?(":") ? "[#{address}]" : address @proxy_uri ||= URI("http://#{ipv6_safe_addr}:#{port}").find_proxy end end end end chef-12.3.0/lib/chef/monkey_patches/net-ssh-multi.rb0000644000004100000410000001161512520074675022270 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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. # # == net-ssh-multi gem patch for concurrency # net-ssh-multi gem has 2 bugs associated with the use of # :concurrent_connections option. # 1-) There is a race condition while fetching the next_session when # :concurrent_connections are set. @open_connections is being # incremented by the connection thread and sometimes # realize_pending_connections!() method can create more than required # connection threads before the @open_connections is set by the # previously created threads. # 2-) When :concurrent_connections is set, server classes are setup # with PendingConnection objects that always return true to busy? # calls. If a connection fails when :concurrent_connections is set, # server ends up returning true to all busy? calls since the session # object is not replaced. Due to this, main event loop (process() # function) never gets terminated. # # See: https://github.com/net-ssh/net-ssh-multi/pull/4 require 'net/ssh/multi/version' if Net::SSH::Multi::Version::STRING == "1.1.0" || Net::SSH::Multi::Version::STRING == "1.2.0" require 'net/ssh/multi' module Net module SSH module Multi class Server # Make sure that server returns false if the ssh connection # has failed. def busy?(include_invisible=false) !failed? && session && session.busy?(include_invisible) end end class Session def next_session(server, force=false) #:nodoc: # don't retry a failed attempt return nil if server.failed? @session_mutex.synchronize do if !force && concurrent_connections && concurrent_connections <= open_connections connection = PendingConnection.new(server) @pending_sessions << connection return connection end # ===== PATCH START # Only increment the open_connections count if the connection # is not being forced. Incase of a force, it will already be # incremented. if !force @open_connections += 1 end # ===== PATCH END end begin server.new_session # I don't understand why this should be necessary--StandardError is a # subclass of Exception, after all--but without explicitly rescuing # StandardError, things like Errno::* and SocketError don't get caught # here! rescue Exception, StandardError => e server.fail! @session_mutex.synchronize { @open_connections -= 1 } case on_error when :ignore then # do nothing when :warn then warn("error connecting to #{server}: #{e.class} (#{e.message})") when Proc then go = catch(:go) { on_error.call(server); nil } case go when nil, :ignore then # nothing when :retry then retry when :raise then raise else warn "unknown 'go' command: #{go.inspect}" end else raise end return nil end end def realize_pending_connections! #:nodoc: return unless concurrent_connections server_list.each do |server| server.close if !server.busy?(true) server.update_session! end @connect_threads.delete_if { |t| !t.alive? } count = concurrent_connections ? (concurrent_connections - open_connections) : @pending_sessions.length count.times do session = @pending_sessions.pop or break # ===== PATCH START # Increment the open_connections count here to prevent # creation of connection thread again before that is # incremented by the thread. @session_mutex.synchronize { @open_connections += 1 } # ===== PATCH END @connect_threads << Thread.new do session.replace_with(next_session(session.server, true)) end end end end end end end end chef-12.3.0/lib/chef/guard_interpreter.rb0000644000004100000410000000221512520074675020267 0ustar www-datawww-data# # Author:: Steven Danna () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/guard_interpreter/default_guard_interpreter' require 'chef/guard_interpreter/resource_guard_interpreter' class Chef class GuardInterpreter def self.for_resource(resource, command, command_opts) if resource.guard_interpreter == :default Chef::GuardInterpreter::DefaultGuardInterpreter.new(command, command_opts) else Chef::GuardInterpreter::ResourceGuardInterpreter.new(resource, command, command_opts) end end end end chef-12.3.0/lib/chef/mash.rb0000644000004100000410000001551112520074675015475 0ustar www-datawww-data# Copyright (c) 2009 Dan Kubb # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # --- # --- # Some portions of blank.rb and mash.rb are verbatim copies of software # licensed under the MIT license. That license is included below: # Copyright (c) 2005-2008 David Heinemeier Hansson # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # This class has dubious semantics and we only have it so that people can write # params[:key] instead of params['key']. class Mash < Hash # @param constructor # The default value for the mash. Defaults to an empty hash. # # @details [Alternatives] # If constructor is a Hash, a new mash will be created based on the keys of # the hash and no default value will be set. def initialize(constructor = {}) if constructor.is_a?(Hash) super() update(constructor) else super(constructor) end end # @param orig Mash being copied # # @return [Object] A new copied Mash def initialize_copy(orig) super # Handle nested values each do |k,v| if v.kind_of?(Mash) || v.is_a?(Array) self[k] = v.dup end end self end # @param key The default value for the mash. Defaults to nil. # # @details [Alternatives] # If key is a Symbol and it is a key in the mash, then the default value will # be set to the value matching the key. def default(key = nil) if key.is_a?(Symbol) && include?(key = key.to_s) self[key] else super end end alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) alias_method :regular_update, :update unless method_defined?(:regular_update) # @param key The key to set. # @param value # The value to set the key to. # # @see Mash#convert_key # @see Mash#convert_value def []=(key, value) regular_writer(convert_key(key), convert_value(value)) end # @param other_hash # A hash to update values in the mash with. The keys and the values will be # converted to Mash format. # # @return [Mash] The updated mash. def update(other_hash) other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } self end alias_method :merge!, :update # @param key The key to check for. This will be run through convert_key. # # @return [Boolean] True if the key exists in the mash. def key?(key) super(convert_key(key)) end # def include? def has_key? def member? alias_method :include?, :key? alias_method :has_key?, :key? alias_method :member?, :key? # @param key The key to fetch. This will be run through convert_key. # @param *extras Default value. # # @return [Object] The value at key or the default value. def fetch(key, *extras) super(convert_key(key), *extras) end # @param *indices # The keys to retrieve values for. These will be run through +convert_key+. # # @return [Array] The values at each of the provided keys def values_at(*indices) indices.collect {|key| self[convert_key(key)]} end # @param hash The hash to merge with the mash. # # @return [Mash] A new mash with the hash values merged in. def merge(hash) self.dup.update(hash) end # @param key # The key to delete from the mash.\ def delete(key) super(convert_key(key)) end # @param *rejected 1, :two => 2, :three => 3 }.except(:one) # #=> { "two" => 2, "three" => 3 } def except(*keys) super(*keys.map {|k| convert_key(k)}) end # Used to provide the same interface as Hash. # # @return [Mash] This mash unchanged. def stringify_keys!; self end # @return [Hash] The mash as a Hash with symbolized keys. def symbolize_keys h = Hash.new(default) each { |key, val| h[key.to_sym] = val } h end # @return [Hash] The mash as a Hash with string keys. def to_hash Hash.new(default).merge(self) end # @return [Mash] Convert a Hash into a Mash # The input Hash's default value is maintained def self.from_hash(hash) mash = Mash.new(hash) mash.default = hash.default mash end protected # @param key The key to convert. # # @param [Object] # The converted key. If the key was a symbol, it will be converted to a # string. # # @api private def convert_key(key) key.kind_of?(Symbol) ? key.to_s : key end # @param value The value to convert. # # @return [Object] # The converted value. A Hash or an Array of hashes, will be converted to # their Mash equivalents. # # @api private def convert_value(value) if value.class == Hash Mash.from_hash(value) elsif value.is_a?(Array) value.collect { |e| convert_value(e) } else value end end end chef-12.3.0/lib/chef/providers.rb0000644000004100000410000001110712520074675016557 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/batch' require 'chef/provider/breakpoint' require 'chef/provider/cookbook_file' require 'chef/provider/cron' require 'chef/provider/cron/solaris' require 'chef/provider/cron/aix' require 'chef/provider/deploy' require 'chef/provider/directory' require 'chef/provider/dsc_script' require 'chef/provider/dsc_resource' require 'chef/provider/env' require 'chef/provider/erl_call' require 'chef/provider/execute' require 'chef/provider/file' require 'chef/provider/git' require 'chef/provider/group' require 'chef/provider/http_request' require 'chef/provider/ifconfig' require 'chef/provider/link' require 'chef/provider/log' require 'chef/provider/ohai' require 'chef/provider/mdadm' require 'chef/provider/mount' require 'chef/provider/package' require 'chef/provider/powershell_script' require 'chef/provider/reboot' require 'chef/provider/remote_directory' require 'chef/provider/remote_file' require 'chef/provider/route' require 'chef/provider/ruby_block' require 'chef/provider/script' require 'chef/provider/service' require 'chef/provider/subversion' require 'chef/provider/template' require 'chef/provider/user' require 'chef/provider/whyrun_safe_ruby_block' require 'chef/provider/env/windows' require 'chef/provider/package/apt' require 'chef/provider/package/dpkg' require 'chef/provider/package/easy_install' require 'chef/provider/package/freebsd/port' require 'chef/provider/package/freebsd/pkg' require 'chef/provider/package/freebsd/pkgng' require 'chef/provider/package/homebrew' require 'chef/provider/package/ips' require 'chef/provider/package/macports' require 'chef/provider/package/openbsd' require 'chef/provider/package/pacman' require 'chef/provider/package/portage' require 'chef/provider/package/paludis' require 'chef/provider/package/rpm' require 'chef/provider/package/rubygems' require 'chef/provider/package/yum' require 'chef/provider/package/zypper' require 'chef/provider/package/solaris' require 'chef/provider/package/smartos' require 'chef/provider/package/aix' require 'chef/provider/service/arch' require 'chef/provider/service/freebsd' require 'chef/provider/service/gentoo' require 'chef/provider/service/init' require 'chef/provider/service/invokercd' require 'chef/provider/service/debian' require 'chef/provider/service/openbsd' require 'chef/provider/service/redhat' require 'chef/provider/service/insserv' require 'chef/provider/service/simple' require 'chef/provider/service/systemd' require 'chef/provider/service/upstart' require 'chef/provider/service/windows' require 'chef/provider/service/solaris' require 'chef/provider/service/macosx' require 'chef/provider/service/aixinit' require 'chef/provider/service/aix' require 'chef/provider/user/dscl' require 'chef/provider/user/pw' require 'chef/provider/user/useradd' require 'chef/provider/user/windows' require 'chef/provider/user/solaris' require 'chef/provider/user/aix' require 'chef/provider/group/aix' require 'chef/provider/group/dscl' require 'chef/provider/group/gpasswd' require 'chef/provider/group/groupadd' require 'chef/provider/group/groupmod' require 'chef/provider/group/pw' require 'chef/provider/group/suse' require 'chef/provider/group/usermod' require 'chef/provider/group/windows' require 'chef/provider/mount/mount' require 'chef/provider/mount/aix' require 'chef/provider/mount/solaris' require 'chef/provider/mount/windows' require 'chef/provider/deploy/revision' require 'chef/provider/deploy/timestamped' require 'chef/provider/remote_file/ftp' require 'chef/provider/remote_file/http' require 'chef/provider/remote_file/local_file' require 'chef/provider/remote_file/fetcher' require "chef/provider/lwrp_base" require 'chef/provider/registry_key' require 'chef/provider/file/content' require 'chef/provider/remote_file/content' require 'chef/provider/cookbook_file/content' require 'chef/provider/template/content' require 'chef/provider/ifconfig/redhat' require 'chef/provider/ifconfig/debian' require 'chef/provider/ifconfig/aix' chef-12.3.0/lib/chef/resource_resolver.rb0000644000004100000410000000720112520074675020312 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/exceptions' require 'chef/platform/resource_priority_map' class Chef class ResourceResolver attr_reader :node attr_reader :resource attr_reader :action def initialize(node, resource) @node = node @resource = resource end # return a deterministically sorted list of Chef::Resource subclasses def resources @resources ||= Chef::Resource.descendants end def resolve maybe_dynamic_resource_resolution(resource) || maybe_chef_platform_lookup(resource) end # this cut looks at if the resource can handle the resource type on the node def enabled_handlers @enabled_handlers ||= resources.select do |klass| klass.provides?(node, resource) end.sort {|a,b| a.to_s <=> b.to_s } @enabled_handlers end private # try dynamically finding a resource based on querying the resources to see what they support def maybe_dynamic_resource_resolution(resource) # log this so we know what resources will work for the generic resource on the node (early cut) Chef::Log.debug "resources for generic #{resource} resource enabled on node include: #{enabled_handlers}" # if none of the resources specifically support the resource, we still need to pick one of the resources that are # enabled on the node to handle the why-run use case. handlers = enabled_handlers if handlers.count >= 2 # this magic stack ranks the resources by where they appear in the resource_priority_map priority_list = [ get_priority_array(node, resource) ].flatten.compact handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i } if priority_list.index(handlers.first).nil? # if we had more than one and we picked one with a precidence of infinity that means that the resource_priority_map # entry for this resource is missing -- we should probably raise here and force resolution of the ambiguity. Chef::Log.warn "Ambiguous resource precedence: #{handlers}, please use Chef.set_resource_priority_array to provide determinism" end handlers = [ handlers.first ] end Chef::Log.debug "resources that survived replacement include: #{handlers}" raise Chef::Exceptions::AmbiguousResourceResolution.new(resource, handlers) if handlers.count >= 2 Chef::Log.debug "dynamic resource resolver FAILED to resolve a resouce" if handlers.empty? return nil if handlers.empty? handlers[0] end # try the old static lookup of resources by mangling name to resource klass def maybe_chef_platform_lookup(resource) Chef::Resource.resource_matching_short_name(resource) end # dep injection hooks def get_priority_array(node, resource_name) resource_priority_map.get_priority_array(node, resource_name) end def resource_priority_map Chef::Platform::ResourcePriorityMap.instance end end end chef-12.3.0/lib/chef/role.rb0000644000004100000410000001721012520074675015504 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # Author:: Christopher Brown () # 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 'chef/config' require 'chef/mixin/params_validate' require 'chef/mixin/from_file' require 'chef/run_list' require 'chef/mash' require 'chef/json_compat' require 'chef/search/query' class Chef class Role include Chef::Mixin::FromFile include Chef::Mixin::ParamsValidate attr_accessor :chef_server_rest # Create a new Chef::Role object. def initialize(chef_server_rest: nil) @name = '' @description = '' @default_attributes = Mash.new @override_attributes = Mash.new @env_run_lists = {"_default" => Chef::RunList.new} @chef_server_rest = chef_server_rest end def chef_server_rest @chef_server_rest ||= Chef::REST.new(Chef::Config[:chef_server_url]) end def self.chef_server_rest Chef::REST.new(Chef::Config[:chef_server_url]) end def name(arg=nil) set_or_return( :name, arg, :regex => /^[\-[:alnum:]_]+$/ ) end def description(arg=nil) set_or_return( :description, arg, :kind_of => String ) end def run_list(*args) if (args.length > 0) @env_run_lists["_default"].reset!(args) end @env_run_lists["_default"] end alias_method :recipes, :run_list # For run_list expansion def run_list_for(environment) if env_run_lists[environment].nil? env_run_lists["_default"] else env_run_lists[environment] end end def active_run_list_for(environment) @env_run_lists.has_key?(environment) ? environment : '_default' end # Per environment run lists def env_run_lists(env_run_lists=nil) if (!env_run_lists.nil?) unless env_run_lists.key?("_default") msg = "_default key is required in env_run_lists.\n" msg << "(env_run_lists: #{env_run_lists.inspect})" raise Chef::Exceptions::InvalidEnvironmentRunListSpecification, msg end @env_run_lists.clear env_run_lists.each { |k,v| @env_run_lists[k] = Chef::RunList.new(*Array(v))} end @env_run_lists end alias :env_run_list :env_run_lists def env_run_lists_add(env_run_lists=nil) if (!env_run_lists.nil?) env_run_lists.each { |k,v| @env_run_lists[k] = Chef::RunList.new(*Array(v))} end @env_run_lists end alias :env_run_list_add :env_run_lists_add def default_attributes(arg=nil) set_or_return( :default_attributes, arg, :kind_of => Hash ) end def override_attributes(arg=nil) set_or_return( :override_attributes, arg, :kind_of => Hash ) end def to_hash env_run_lists_without_default = @env_run_lists.dup env_run_lists_without_default.delete("_default") result = { "name" => @name, "description" => @description, 'json_class' => self.class.name, "default_attributes" => @default_attributes, "override_attributes" => @override_attributes, "chef_type" => "role", #Render to_json correctly for run_list items (both run_list and evn_run_lists) #so malformed json does not result "run_list" => run_list.run_list.map { |item| item.to_s }, "env_run_lists" => env_run_lists_without_default.inject({}) do |accumulator, (k, v)| accumulator[k] = v.map { |x| x.to_s } accumulator end } result end # Serialize this object as a hash def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def update_from!(o) description(o.description) recipes(o.recipes) if defined?(o.recipes) default_attributes(o.default_attributes) override_attributes(o.override_attributes) env_run_lists(o.env_run_lists) unless o.env_run_lists.nil? self end # Create a Chef::Role from JSON def self.json_create(o) role = new role.name(o["name"]) role.description(o["description"]) role.default_attributes(o["default_attributes"]) role.override_attributes(o["override_attributes"]) # _default run_list is in 'run_list' for newer clients, and # 'recipes' for older clients. env_run_list_hash = {"_default" => (o.has_key?("run_list") ? o["run_list"] : o["recipes"])} # Clients before 0.10 do not include env_run_lists, so only # merge if it's there. if o["env_run_lists"] env_run_list_hash.merge!(o["env_run_lists"]) end role.env_run_lists(env_run_list_hash) role end # Get the list of all roles from the API. def self.list(inflate=false) if inflate response = Hash.new Chef::Search::Query.new.search(:role) do |n| response[n.name] = n unless n.nil? end response else chef_server_rest.get_rest("roles") end end # Load a role by name from the API def self.load(name) chef_server_rest.get_rest("roles/#{name}") end def environment(env_name) chef_server_rest.get_rest("roles/#{@name}/environments/#{env_name}") end def environments chef_server_rest.get_rest("roles/#{@name}/environments") end # Remove this role via the REST API def destroy chef_server_rest.delete_rest("roles/#{@name}") end # Save this role via the REST API def save begin chef_server_rest.put_rest("roles/#{@name}", self) rescue Net::HTTPServerException => e raise e unless e.response.code == "404" chef_server_rest.post_rest("roles", self) end self end # Create the role via the REST API def create chef_server_rest.post_rest("roles", self) self end # As a string def to_s "role[#{@name}]" end # Load a role from disk - prefers to load the JSON, but will happily load # the raw rb files as well. Can search within directories in the role_path. def self.from_disk(name) paths = Array(Chef::Config[:role_path]) paths.each do |path| roles_files = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "**", "**")) js_files = roles_files.select { |file| file.match(/\/#{name}\.json$/) } rb_files = roles_files.select { |file| file.match(/\/#{name}\.rb$/) } if js_files.count > 1 or rb_files.count > 1 raise Chef::Exceptions::DuplicateRole, "Multiple roles of same type found named #{name}" end js_path, rb_path = js_files.first, rb_files.first if js_path && File.exists?(js_path) # from_json returns object.class => json_class in the JSON. return Chef::JSONCompat.from_json(IO.read(js_path)) elsif rb_path && File.exists?(rb_path) role = Chef::Role.new role.name(name) role.from_file(rb_path) return role end end raise Chef::Exceptions::RoleNotFound, "Role '#{name}' could not be loaded from disk" end end end chef-12.3.0/lib/chef/win32/0000755000004100000410000000000012520074675015157 5ustar www-datawww-datachef-12.3.0/lib/chef/win32/file/0000755000004100000410000000000012520074675016076 5ustar www-datawww-datachef-12.3.0/lib/chef/win32/file/info.rb0000644000004100000410000000623212520074675017361 0ustar www-datawww-data# # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/file' class Chef module ReservedNames::Win32 class File # Objects of class Chef::ReservedNames::Win32::File::Stat encapsulate common status # information for Chef::ReservedNames::Win32::File objects. The information # is recorded at the moment the Chef::ReservedNames::Win32::File::Stat object is # created; changes made to the file after that point will not be reflected. class Info include Chef::ReservedNames::Win32::API::File include Chef::ReservedNames::Win32::API # http://msdn.microsoft.com/en-us/library/windows/desktop/aa363788(v=vs.85).aspx def initialize(file_name) raise Errno::ENOENT, file_name unless ::File.exist?(file_name) @file_info = retrieve_file_info(file_name) end def volume_serial_number @file_info[:dw_volume_serial_number] end def index make_uint64(@file_info[:n_file_index_low], @file_info[:n_file_index_high]) end def last_access_time parse_time(@file_info[:ft_last_access_time]) end def creation_time parse_time(@file_info[:ft_creation_time]) end def last_write_time parse_time(@file_info[:ft_last_write_time]) end def links @file_info[:n_number_of_links] end def size make_uint64(@file_info[:n_file_size_low], @file_info[:n_file_size_high]) end ############################## # ::File::Stat compat alias :atime :last_access_time alias :mtime :last_write_time alias :ctime :creation_time # we're faking it here, but this is in the spirit of ino in *nix # # from MSDN: # # "The identifier (low and high parts) and the volume serial number # uniquely identify a file on a single computer. To determine whether # two open handles represent the same file, combine the identifier # and the volume serial number for each file and compare them."" # def ino volume_serial_number + index end ############################## # given a +Chef::ReservedNames::Win32::API::File::FILETIME+ structure convert into a # Ruby +Time+ object. # def parse_time(file_time_struct) wtime_to_time(make_uint64(file_time_struct[:dw_low_date_time], file_time_struct[:dw_high_date_time])) end end end end end chef-12.3.0/lib/chef/win32/handle.rb0000644000004100000410000000373612520074675016750 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api/process' require 'chef/win32/api/psapi' require 'chef/win32/api/system' require 'chef/win32/error' class Chef module ReservedNames::Win32 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)) end attr_reader :handle def self.close_handle_finalizer(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) unless CloseHandle(handle) Chef::ReservedNames::Win32::Error.raise! end end end end end chef-12.3.0/lib/chef/win32/mutex.rb0000644000004100000410000001024212520074675016645 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright 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/win32/api/synchronization' class Chef module ReservedNames::Win32 class Mutex include Chef::ReservedNames::Win32::API::Synchronization def initialize(name) @name = name create_system_mutex end attr_reader :handle attr_reader :name ##################################################### # Attempts to grab the mutex. # Returns true if the mutex is grabbed or if it's already # owned; false otherwise. def test WaitForSingleObject(handle, 0) == WAIT_OBJECT_0 end ##################################################### # Attempts to grab the mutex and waits until it is acquired. def wait loop do wait_result = WaitForSingleObject(handle, 1000) case wait_result when WAIT_TIMEOUT # We are periodically waking up in order to give ruby a # chance to process any signal it got while we were # sleeping. This condition shouldn't contain any logic # other than sleeping. sleep 0.1 when WAIT_ABANDONED # Previous owner of the mutex died before it can release the # mutex. Log a warning and continue. Chef::Log.debug "Existing owner of the mutex exited prematurely." break when WAIT_OBJECT_0 # Mutex is successfully acquired. break else Chef::Log.error("Failed to acquire system mutex '#{name}'. Return code: #{wait_result}") Chef::ReservedNames::Win32::Error.raise! end end end ##################################################### # Releaes the mutex def release # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685066(v=vs.85).aspx # Note that release method needs to be called more than once # if mutex is acquired more than once. unless ReleaseMutex(handle) # Don't fail things in here if we can't release the mutex. # Because it will be automatically released when the owner # of the process goes away and this class is only being used # to synchronize chef-clients runs on a node. Chef::Log.error("Can not release mutex '#{name}'. This might cause issues \ if the mutex is attempted to be acquired by other threads.") Chef::ReservedNames::Win32::Error.raise! end end private def create_system_mutex # First check if there exists a mutex in the system with the # given name. We need only synchronize rights if a mutex is # already created. # InheritHandle is set to true so that subprocesses can # inherit the ownership of the mutex. @handle = OpenMutexW(SYNCHRONIZE, true, name.to_wstring) if @handle == 0 # Mutext doesn't exist so create one. # In the initial creation of the mutex initial_owner is set to # false so that mutex will not be acquired until someone calls # acquire. # In order to call "*W" windows apis, strings needs to be # encoded as wide strings. @handle = CreateMutexW(nil, false, name.to_wstring) # Looks like we can't create the mutex for some reason. # Fail early. if @handle == 0 Chef::Log.error("Failed to create system mutex with name'#{name}'") Chef::ReservedNames::Win32::Error.raise! end end end end end end chef-12.3.0/lib/chef/win32/security/0000755000004100000410000000000012520074675017026 5ustar www-datawww-datachef-12.3.0/lib/chef/win32/security/ace.rb0000644000004100000410000000732012520074675020105 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/security/sid' require 'chef/win32/memory' require 'ffi' class Chef module ReservedNames::Win32 class Security class ACE def initialize(pointer, owner = nil) if Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.supports?(pointer.read_uchar) @struct = Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.new pointer else # TODO Support ALL the things @struct = Chef::ReservedNames::Win32::API::Security::ACE_HEADER.new pointer end # Keep a reference to the actual owner of this memory so we don't get freed @owner = owner end def self.size_with_sid(sid) Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.offset_of(:SidStart) + sid.size end def self.access_allowed(sid, mask, flags = 0) create_ace_with_mask_and_sid(Chef::ReservedNames::Win32::API::Security::ACCESS_ALLOWED_ACE_TYPE, flags, mask, sid) end def self.access_denied(sid, mask, flags = 0) create_ace_with_mask_and_sid(Chef::ReservedNames::Win32::API::Security::ACCESS_DENIED_ACE_TYPE, flags, mask, sid) end attr_reader :struct def ==(other) type == other.type && flags == other.flags && mask == other.mask && sid == other.sid end def dup ACE.create_ace_with_mask_and_sid(type, flags, mask, sid) end def flags struct[:AceFlags] end def flags=(val) struct[:AceFlags] = val end def explicit? ! inherited? end def inherited? (struct[:AceFlags] & Chef::ReservedNames::Win32::API::Security::INHERITED_ACE) != 0 end def mask struct[:Mask] end def mask=(val) struct[:Mask] = val end def pointer struct.pointer end def size struct[:AceSize] end def sid # The SID runs off the end of the structure, starting at :SidStart. # Use pointer arithmetic to get a pointer to that location. Chef::ReservedNames::Win32::Security::SID.new(struct.pointer + struct.offset_of(:SidStart)) end def to_s "#{sid.account_name}/flags:#{flags.to_s(16)}/mask:#{mask.to_s(16)}" end def type struct[:AceType] end def self.create_ace_with_mask_and_sid(type, flags, mask, sid) size_needed = size_with_sid(sid) pointer = FFI::MemoryPointer.new size_needed struct = Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.new pointer struct[:AceType] = type struct[:AceFlags] = flags struct[:AceSize] = size_needed struct[:Mask] = mask Chef::ReservedNames::Win32::Memory.memcpy(struct.pointer + struct.offset_of(:SidStart), sid.pointer, sid.size) ACE.new(struct.pointer) end end end end end chef-12.3.0/lib/chef/win32/security/sid.rb0000644000004100000410000001745212520074675020143 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/api/net' require 'chef/win32/api/error' require 'wmi-lite/wmi' class Chef module ReservedNames::Win32 class Security class SID include Chef::ReservedNames::Win32::API::Net include Chef::ReservedNames::Win32::API::Error class << self include Chef::ReservedNames::Win32::API::Net include Chef::ReservedNames::Win32::API::Error end def initialize(pointer, owner = nil) @pointer = pointer # Keep a reference to the actual owner of this memory so we don't get freed @owner = owner end def self.from_account(name) domain, sid, use = Chef::ReservedNames::Win32::Security.lookup_account_name(name) sid end def self.from_string_sid(string_sid) Chef::ReservedNames::Win32::Security::convert_string_sid_to_sid(string_sid) end def ==(other) other != nil && Chef::ReservedNames::Win32::Security.equal_sid(self, other) end attr_reader :pointer def account Chef::ReservedNames::Win32::Security.lookup_account_sid(self) end def account_name domain, name, use = account (domain != nil && domain.length > 0) ? "#{domain}\\#{name}" : name end def size Chef::ReservedNames::Win32::Security.get_length_sid(self) end def to_s Chef::ReservedNames::Win32::Security.convert_sid_to_string_sid(self) end def valid? Chef::ReservedNames::Win32::Security.is_valid_sid(self) end # Well-known SIDs def self.Null SID.from_string_sid('S-1-0') end def self.Nobody SID.from_string_sid('S-1-0-0') end def self.World SID.from_string_sid('S-1-1') end def self.Everyone SID.from_string_sid('S-1-1-0') end def self.Local SID.from_string_sid('S-1-2') end def self.Creator SID.from_string_sid('S-1-3') end def self.CreatorOwner SID.from_string_sid('S-1-3-0') end def self.CreatorGroup SID.from_string_sid('S-1-3-1') end def self.CreatorOwnerServer SID.from_string_sid('S-1-3-2') end def self.CreatorGroupServer SID.from_string_sid('S-1-3-3') end def self.NonUnique SID.from_string_sid('S-1-4') end def self.Nt SID.from_string_sid('S-1-5') end def self.Dialup SID.from_string_sid('S-1-5-1') end def self.Network SID.from_string_sid('S-1-5-2') end def self.Batch SID.from_string_sid('S-1-5-3') end def self.Interactive SID.from_string_sid('S-1-5-4') end def self.Service SID.from_string_sid('S-1-5-6') end def self.Anonymous SID.from_string_sid('S-1-5-7') end def self.Proxy SID.from_string_sid('S-1-5-8') end def self.EnterpriseDomainControllers SID.from_string_sid('S-1-5-9') end def self.PrincipalSelf SID.from_string_sid('S-1-5-10') end def self.AuthenticatedUsers SID.from_string_sid('S-1-5-11') end def self.RestrictedCode SID.from_string_sid('S-1-5-12') end def self.TerminalServerUsers SID.from_string_sid('S-1-5-13') end def self.LocalSystem SID.from_string_sid('S-1-5-18') end def self.NtLocal SID.from_string_sid('S-1-5-19') end def self.NtNetwork SID.from_string_sid('S-1-5-20') end def self.BuiltinAdministrators SID.from_string_sid('S-1-5-32-544') end def self.BuiltinUsers SID.from_string_sid('S-1-5-32-545') end def self.Guests SID.from_string_sid('S-1-5-32-546') end def self.PowerUsers SID.from_string_sid('S-1-5-32-547') end def self.AccountOperators SID.from_string_sid('S-1-5-32-548') end def self.ServerOperators SID.from_string_sid('S-1-5-32-549') end def self.PrintOperators SID.from_string_sid('S-1-5-32-550') end def self.BackupOperators SID.from_string_sid('S-1-5-32-551') end def self.Replicators SID.from_string_sid('S-1-5-32-552') end def self.Administrators SID.from_string_sid('S-1-5-32-544') end def self.None SID.from_account("#{::ENV['COMPUTERNAME']}\\None") end def self.Administrator SID.from_account("#{::ENV['COMPUTERNAME']}\\#{SID.admin_account_name}") end def self.Guest SID.from_account("#{::ENV['COMPUTERNAME']}\\Guest") end def self.current_user SID.from_account("#{::ENV['USERDOMAIN']}\\#{::ENV['USERNAME']}") end def self.admin_account_name @admin_account_name ||= begin admin_account_name = nil # Call NetUserEnum to enumerate the users without hitting network # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370652(v=vs.85).aspx servername = nil # We are querying the local server level = 3 # We want USER_INFO_3 structure which contains the SID filter = FILTER_NORMAL_ACCOUNT # Only query the user accounts bufptr = FFI::MemoryPointer.new(:pointer) # Buffer which will receive the data prefmaxlen = MAX_PREFERRED_LENGTH # Let the system allocate the needed amount of memory entriesread = FFI::Buffer.new(:long).write_long(0) totalentries = FFI::Buffer.new(:long).write_long(0) resume_handle = FFI::Buffer.new(:long).write_long(0) status = ERROR_MORE_DATA while(status == ERROR_MORE_DATA) do status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle) if (status == NERR_Success || status == ERROR_MORE_DATA) entriesread.read_long.times.collect do |i| user_info = USER_INFO_3.new(bufptr.read_pointer + i * USER_INFO_3.size) # Check if the account is the Administrator account # RID for the Administrator account is always 500 and it's privilage is set to USER_PRIV_ADMIN if user_info[:usri3_user_id] == 500 && user_info[:usri3_priv] == 2 # USER_PRIV_ADMIN (2) - Administrator admin_account_name = user_info[:usri3_name].read_wstring break end end # Free the memory allocated by the system NetApiBufferFree(bufptr.read_pointer) end end raise "Can not determine the administrator account name." if admin_account_name.nil? admin_account_name end end end end end end chef-12.3.0/lib/chef/win32/security/acl.rb0000644000004100000410000000565312520074675020123 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/security/ace' require 'ffi' class Chef module ReservedNames::Win32 class Security class ACL include Enumerable def initialize(pointer, owner = nil) @struct = Chef::ReservedNames::Win32::API::Security::ACLStruct.new pointer # Keep a reference to the actual owner of this memory so that it isn't freed out from under us # TODO this could be avoided if we could mark a pointer's parent manually @owner = owner end def self.create(aces) aces_size = aces.inject(0) { |sum,ace| sum + ace.size } acl_size = align_dword(Chef::ReservedNames::Win32::API::Security::ACLStruct.size + aces_size) # What the heck is 94??? acl = Chef::ReservedNames::Win32::Security.initialize_acl(acl_size) aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(acl, ace) } acl end attr_reader :struct def ==(other) return false if length != other.length 0.upto(length-1) do |i| return false if self[i] != other[i] end return true end def pointer struct.pointer end def [](index) Chef::ReservedNames::Win32::Security.get_ace(self, index) end def delete_at(index) Chef::ReservedNames::Win32::Security.delete_ace(self, index) end def each 0.upto(length-1) { |i| yield self[i] } end def insert(index, *aces) aces.reverse_each { |ace| add_ace(self, ace, index) } end def length struct[:AceCount] end def push(*aces) aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(self, ace) } end def unshift(*aces) aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(self, ace, 0) } end def valid? Chef::ReservedNames::Win32::Security.is_valid_acl(self) end def to_s "[#{self.collect { |ace| ace.to_s }.join(", ")}]" end def self.align_dword(size) (size + 4 - 1) & 0xfffffffc end private_class_method :align_dword end end end end chef-12.3.0/lib/chef/win32/security/securable_object.rb0000644000004100000410000001227512520074675022655 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/security/acl' require 'chef/win32/security/sid' class Chef module ReservedNames::Win32 class Security class SecurableObject def initialize(path, type = :SE_FILE_OBJECT) @path = path @type = type end attr_reader :path attr_reader :type SecurityConst = Chef::ReservedNames::Win32::API::Security # This method predicts what the rights mask would be on an object # if you created an ACE with the given mask. Specifically, it looks for # generic attributes like GENERIC_READ, and figures out what specific # attributes will be set. This is important if you want to try to # compare an existing ACE with one you want to create. def predict_rights_mask(generic_mask) mask = generic_mask #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_READ if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0 #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_WRITE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0 #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_EXECUTE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0 #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_ALL if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0 if type == :SE_FILE_OBJECT mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0 mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0 mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0 mask |= Chef::ReservedNames::Win32::API::Security::FILE_ALL_ACCESS if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0 else raise "Unimplemented object type for predict_security_mask: #{type}" end mask &= ~(Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) mask end def security_descriptor(include_sacl = false) security_information = Chef::ReservedNames::Win32::API::Security::OWNER_SECURITY_INFORMATION | Chef::ReservedNames::Win32::API::Security::GROUP_SECURITY_INFORMATION | Chef::ReservedNames::Win32::API::Security::DACL_SECURITY_INFORMATION if include_sacl security_information |= Chef::ReservedNames::Win32::API::Security::SACL_SECURITY_INFORMATION Security.with_privileges("SeSecurityPrivilege") do Security.get_named_security_info(path, type, security_information) end else Security.get_named_security_info(path, type, security_information) end end def dacl=(val) Security.set_named_security_info(path, type, :dacl => val) end # You don't set dacl_inherits without also setting dacl, # because Windows gets angry and denies you access. So # if you want to do that, you may as well do both at once. def set_dacl(dacl, dacl_inherits) Security.set_named_security_info(path, type, :dacl => dacl, :dacl_inherits => dacl_inherits) end def group=(val) Security.set_named_security_info(path, type, :group => val) end def owner=(val) # TODO to fix serious permissions problems, we may need to enable SeBackupPrivilege. But we might need it (almost) everywhere else, too. Security.with_privileges("SeTakeOwnershipPrivilege", "SeRestorePrivilege") do Security.set_named_security_info(path, type, :owner => val) end end def sacl=(val) Security.with_privileges("SeSecurityPrivilege") do Security.set_named_security_info(path, type, :sacl => val) end end def set_sacl(sacl, sacl_inherits) Security.with_privileges("SeSecurityPrivilege") do Security.set_named_security_info(path, type, :sacl => sacl, :sacl_inherits => sacl_inherits) end end end end end end chef-12.3.0/lib/chef/win32/security/security_descriptor.rb0000644000004100000410000000530512520074675023463 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/security/acl' require 'chef/win32/security/sid' class Chef module ReservedNames::Win32 class Security class SecurityDescriptor def initialize(pointer) @pointer = pointer end attr_reader :pointer def absolute? !self_relative? end def control control, version = Chef::ReservedNames::Win32::Security.get_security_descriptor_control(self) control end def dacl raise "DACL not present" if !dacl_present? present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_dacl(self) acl end def dacl_inherits? (control & Chef::ReservedNames::Win32::API::Security::SE_DACL_PROTECTED) == 0 end def dacl_present? (control & Chef::ReservedNames::Win32::API::Security::SE_DACL_PRESENT) != 0 end def group result, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_group(self) result end def owner result, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_owner(self) result end def sacl raise "SACL not present" if !sacl_present? Security.with_privileges("SeSecurityPrivilege") do present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_sacl(self) acl end end def sacl_inherits? (control & Chef::ReservedNames::Win32::API::Security::SE_SACL_PROTECTED) == 0 end def sacl_present? (control & Chef::ReservedNames::Win32::API::Security::SE_SACL_PRESENT) != 0 end def self_relative? (control & Chef::ReservedNames::Win32::API::Security::SE_SELF_RELATIVE) != 0 end def valid? Chef::ReservedNames::Win32::Security.is_valid_security_descriptor(self) end end end end end chef-12.3.0/lib/chef/win32/security/token.rb0000644000004100000410000000560412520074675020500 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/api/security' require 'ffi' class Chef module ReservedNames::Win32 class Security class Token def initialize(handle) @handle = handle end attr_reader :handle def enable_privileges(*privilege_names) # Build the list of privileges we want to set new_privileges = Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.new( FFI::MemoryPointer.new(Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.size_with_privileges(privilege_names.length))) new_privileges[:PrivilegeCount] = 0 privilege_names.each do |privilege_name| luid = Chef::ReservedNames::Win32::API::Security::LUID.new # Ignore failure (with_privileges TRIES but does not guarantee success-- # APIs down the line will fail if privilege escalation fails) if Chef::ReservedNames::Win32::API::Security.LookupPrivilegeValueW(nil, privilege_name.to_wstring, luid) new_privilege = new_privileges.privilege(new_privileges[:PrivilegeCount]) new_privilege[:Luid][:LowPart] = luid[:LowPart] new_privilege[:Luid][:HighPart] = luid[:HighPart] new_privilege[:Attributes] = Chef::ReservedNames::Win32::API::Security::SE_PRIVILEGE_ENABLED new_privileges[:PrivilegeCount] = new_privileges[:PrivilegeCount] + 1 end end old_privileges = Chef::ReservedNames::Win32::Security.adjust_token_privileges(self, new_privileges) end def adjust_privileges(privileges_struct) if privileges_struct[:PrivilegeCount] > 0 Chef::ReservedNames::Win32::Security::adjust_token_privileges(self, privileges_struct) end end def duplicate_token(security_impersonation_level) duplicate_token_handle = FFI::Buffer.new(:ulong) unless Chef::ReservedNames::Win32::API::Security.DuplicateToken(handle.handle, security_impersonation_level, duplicate_token_handle) raise Chef::ReservedNames::Win32::Error.raise! end Token.new(Handle.new(duplicate_token_handle.read_ulong)) end end end end end chef-12.3.0/lib/chef/win32/process.rb0000644000004100000410000000467112520074675017172 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api/process' require 'chef/win32/api/psapi' require 'chef/win32/error' require 'chef/win32/handle' require 'ffi' class Chef module ReservedNames::Win32 class Process include Chef::ReservedNames::Win32::API::Process extend Chef::ReservedNames::Win32::API::Process include Chef::ReservedNames::Win32::API::PSAPI extend Chef::ReservedNames::Win32::API::PSAPI def initialize(handle) @handle = handle end attr_reader :handle def id Process.get_process_id(handle) end def handle_count Process.get_process_handle_count(handle) end def memory_info Process.get_process_memory_info(handle) end def self.get_current_process Process.new(Handle.new(GetCurrentProcess())) end def self.get_process_handle_count(handle) handle_count = FFI::MemoryPointer.new :uint32 unless GetProcessHandleCount(handle.handle, handle_count) Chef::ReservedNames::Win32::Error.raise! end handle_count.read_uint32 end def self.get_process_id(handle) # Must have PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION rights result = GetProcessId(handle.handle) if result == 0 Chef::ReservedNames::Win32::Error.raise! end result end # Must have PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION rights, # AND the PROCESS_VM_READ right def self.get_process_memory_info(handle) memory_info = PROCESS_MEMORY_COUNTERS.new unless GetProcessMemoryInfo(handle.handle, memory_info, memory_info.size) Chef::ReservedNames::Win32::Error.raise! end memory_info end end end end chef-12.3.0/lib/chef/win32/crypto.rb0000644000004100000410000000307412520074675017030 0ustar www-datawww-data# # Author:: Jay Mundrawala () # Copyright:: Copyright 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/win32/error' require 'chef/win32/api/memory' require 'chef/win32/api/crypto' require 'digest' class Chef module ReservedNames::Win32 class Crypto include Chef::ReservedNames::Win32::API::Crypto extend Chef::ReservedNames::Win32::API::Crypto def self.encrypt(str, &block) data_blob = CRYPT_INTEGER_BLOB.new unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, 0, data_blob) Chef::ReservedNames::Win32::Error.raise! end bytes = data_blob[:pbData].get_bytes(0, data_blob[:cbData]) if block block.call(bytes) else Digest.hexencode(bytes) end ensure unless data_blob[:pbData].null? Chef::ReservedNames::Win32::Memory.local_free(data_blob[:pbData]) end end end end end chef-12.3.0/lib/chef/win32/memory.rb0000644000004100000410000000604112520074675017015 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/error' require 'chef/win32/api/memory' class Chef module ReservedNames::Win32 class Memory include Chef::ReservedNames::Win32::API::Memory extend Chef::ReservedNames::Win32::API::Memory # local_alloc(length[, flags]) [BLOCK] # Allocates memory using LocalAlloc # If BLOCK is specified, the memory will be passed # to the block and freed afterwards. def self.local_alloc(length, flags = LPTR, &block) result = LocalAlloc(flags, length) if result.null? Chef::ReservedNames::Win32::Error.raise! end # If a block is passed, handle freeing the memory at the end if block != nil begin yield result ensure local_free(result) end else result end end # local_discard(pointer) # Discard memory. Equivalent to local_realloc(pointer, 0) def self.local_discard(pointer) local_realloc(pointer, 0, LMEM_MOVEABLE) end # local_flags(pointer) # Get lock count and Windows flags for local_alloc allocated memory. # Use: flags, lock_count = local_flags(pointer) def self.local_flags(pointer) result = LocalFlags(pointer) if result == LMEM_INVALID_HANDLE Chef::ReservedNames::Win32::Error.raise! end [ result & ~LMEM_LOCKCOUNT, result & LMEM_LOCKCOUNT ] end # local_free(pointer) # Free memory allocated using local_alloc def self.local_free(pointer) result = LocalFree(pointer) if !result.null? Chef::ReservedNames::Win32::Error.raise! end end # local_realloc(pointer, size[, flags]) # Resizes memory allocated using LocalAlloc. def self.local_realloc(pointer, size, flags = LMEM_MOVEABLE | LMEM_ZEROINIT) result = LocalReAlloc(pointer, size, flags) if result.null? Chef::ReservedNames::Win32::Error.raise! end result end # local_size(pointer) # Gets the size of memory allocated using LocalAlloc. def self.local_size(pointer) result = LocalSize(pointer) if result == 0 Chef::ReservedNames::Win32::Error.raise! end result end def self.local_free_finalizer(pointer) proc { local_free(pointer) } end end end end chef-12.3.0/lib/chef/win32/security.rb0000644000004100000410000005635712520074675017373 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api/security' require 'chef/win32/error' require 'chef/win32/memory' require 'chef/win32/process' require 'chef/win32/unicode' require 'chef/win32/security/token' class Chef module ReservedNames::Win32 class Security include Chef::ReservedNames::Win32::API::Error extend Chef::ReservedNames::Win32::API::Error include Chef::ReservedNames::Win32::API::Security extend Chef::ReservedNames::Win32::API::Security extend Chef::ReservedNames::Win32::API::Macros def self.access_check(security_descriptor, token, desired_access, generic_mapping) token_handle = token.handle.handle security_descriptor_ptr = security_descriptor.pointer rights_ptr = FFI::MemoryPointer.new(:ulong) rights_ptr.write_ulong(desired_access) # This function takes care of calling MapGenericMask, so you don't have to MapGenericMask(rights_ptr, generic_mapping) result_ptr = FFI::MemoryPointer.new(:ulong) # Because optional actually means required privileges = PRIVILEGE_SET.new privileges[:PrivilegeCount] = 0 privileges_length_ptr = FFI::MemoryPointer.new(:ulong) privileges_length_ptr.write_ulong(privileges.size) granted_access_ptr = FFI::MemoryPointer.new(:ulong) unless AccessCheck(security_descriptor_ptr, token_handle, rights_ptr.read_ulong, generic_mapping, privileges, privileges_length_ptr, granted_access_ptr, result_ptr) Chef::ReservedNames::Win32::Error.raise! end result_ptr.read_ulong == 1 end def self.add_ace(acl, ace, insert_position = MAXDWORD, revision = ACL_REVISION) acl = acl.pointer if acl.respond_to?(:pointer) ace = ace.pointer if ace.respond_to?(:pointer) ace_size = ACE_HEADER.new(ace)[:AceSize] unless AddAce(acl, revision, insert_position, ace, ace_size) Chef::ReservedNames::Win32::Error.raise! end end def self.add_access_allowed_ace(acl, sid, access_mask, revision = ACL_REVISION) acl = acl.pointer if acl.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless AddAccessAllowedAce(acl, revision, access_mask, sid) Chef::ReservedNames::Win32::Error.raise! end end def self.add_access_allowed_ace_ex(acl, sid, access_mask, flags = 0, revision = ACL_REVISION) acl = acl.pointer if acl.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless AddAccessAllowedAceEx(acl, revision, flags, access_mask, sid) Chef::ReservedNames::Win32::Error.raise! end end def self.add_access_denied_ace(acl, sid, access_mask, revision = ACL_REVISION) acl = acl.pointer if acl.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless AddAccessDeniedAce(acl, revision, access_mask, sid) Chef::ReservedNames::Win32::Error.raise! end end def self.add_access_denied_ace_ex(acl, sid, access_mask, flags = 0, revision = ACL_REVISION) acl = acl.pointer if acl.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless AddAccessDeniedAceEx(acl, revision, flags, access_mask, sid) Chef::ReservedNames::Win32::Error.raise! end end def self.adjust_token_privileges(token, privileges) token = token.handle if token.respond_to?(:handle) old_privileges_size = FFI::Buffer.new(:long).write_long(privileges.size_with_privileges) old_privileges = TOKEN_PRIVILEGES.new(FFI::Buffer.new(old_privileges_size.read_long)) unless AdjustTokenPrivileges(token.handle, false, privileges, privileges.size_with_privileges, old_privileges, old_privileges_size) Chef::ReservedNames::Win32::Error.raise! end old_privileges end def self.convert_sid_to_string_sid(sid) sid = sid.pointer if sid.respond_to?(:pointer) result = FFI::MemoryPointer.new :pointer # TODO: use the W version unless ConvertSidToStringSidA(sid, result) Chef::ReservedNames::Win32::Error.raise! end result_string = result.read_pointer.read_string Chef::ReservedNames::Win32::Memory.local_free(result.read_pointer) result_string end def self.convert_string_sid_to_sid(string_sid) result = FFI::MemoryPointer.new :pointer unless ConvertStringSidToSidW(string_sid.to_wstring, result) Chef::ReservedNames::Win32::Error.raise! end result_pointer = result.read_pointer sid = SID.new(result_pointer) # The result pointer must be freed with local_free ObjectSpace.define_finalizer(sid, Memory.local_free_finalizer(result_pointer)) sid end def self.delete_ace(acl, index) acl = acl.pointer if acl.respond_to?(:pointer) unless DeleteAce(acl, index) Chef::ReservedNames::Win32::Error.raise! end end def self.equal_sid(sid1, sid2) sid1 = sid1.pointer if sid1.respond_to?(:pointer) sid2 = sid2.pointer if sid2.respond_to?(:pointer) EqualSid(sid1, sid2) end def self.free_sid(sid) sid = sid.pointer if sid.respond_to?(:pointer) unless FreeSid(sid).null? Chef::ReservedNames::Win32::Error.raise! end end def self.get_ace(acl, index) acl = acl.pointer if acl.respond_to?(:pointer) ace = FFI::Buffer.new :pointer unless GetAce(acl, index, ace) Chef::ReservedNames::Win32::Error.raise! end ACE.new(ace.read_pointer, acl) end def self.get_length_sid(sid) sid = sid.pointer if sid.respond_to?(:pointer) GetLengthSid(sid) end def self.get_file_security(path, info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION) size_ptr = FFI::MemoryPointer.new(:ulong) success = GetFileSecurityW(path.to_wstring, info, nil, 0, size_ptr) if !success && FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end security_descriptor_ptr = FFI::MemoryPointer.new(size_ptr.read_ulong) unless GetFileSecurityW(path.to_wstring, info, security_descriptor_ptr, size_ptr.read_ulong, size_ptr) Chef::ReservedNames::Win32::Error.raise! end SecurityDescriptor.new(security_descriptor_ptr) end def self.get_named_security_info(path, type = :SE_FILE_OBJECT, info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION) security_descriptor = FFI::MemoryPointer.new :pointer hr = GetNamedSecurityInfoW(path.to_wstring, type, info, nil, nil, nil, nil, security_descriptor) if hr != ERROR_SUCCESS Chef::ReservedNames::Win32::Error.raise!("get_named_security_info(#{path}, #{type}, #{info})") end result_pointer = security_descriptor.read_pointer result = SecurityDescriptor.new(result_pointer) # This memory has to be freed with LocalFree. ObjectSpace.define_finalizer(result, Memory.local_free_finalizer(result_pointer)) result end def self.get_security_descriptor_control(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) result = FFI::Buffer.new :ushort version = FFI::Buffer.new :uint32 unless GetSecurityDescriptorControl(security_descriptor, result, version) Chef::ReservedNames::Win32::Error.raise! end [ result.read_ushort, version.read_uint32 ] end def self.get_security_descriptor_dacl(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) present = FFI::Buffer.new :bool defaulted = FFI::Buffer.new :bool acl = FFI::Buffer.new :pointer unless GetSecurityDescriptorDacl(security_descriptor, present, acl, defaulted) Chef::ReservedNames::Win32::Error.raise! end acl = acl.read_pointer [ present.read_char != 0, acl.null? ? nil : ACL.new(acl, security_descriptor), defaulted.read_char != 0 ] end def self.get_security_descriptor_group(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) result = FFI::Buffer.new :pointer defaulted = FFI::Buffer.new :long unless GetSecurityDescriptorGroup(security_descriptor, result, defaulted) Chef::ReservedNames::Win32::Error.raise! end sid = SID.new(result.read_pointer, security_descriptor) defaulted = defaulted.read_char != 0 [ sid, defaulted ] end def self.get_security_descriptor_owner(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) result = FFI::Buffer.new :pointer defaulted = FFI::Buffer.new :long unless GetSecurityDescriptorOwner(security_descriptor, result, defaulted) Chef::ReservedNames::Win32::Error.raise! end sid = SID.new(result.read_pointer, security_descriptor) defaulted = defaulted.read_char != 0 [ sid, defaulted ] end def self.get_security_descriptor_sacl(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) present = FFI::Buffer.new :bool defaulted = FFI::Buffer.new :bool acl = FFI::Buffer.new :pointer unless GetSecurityDescriptorSacl(security_descriptor, present, acl, defaulted) Chef::ReservedNames::Win32::Error.raise! end acl = acl.read_pointer [ present.read_char != 0, acl.null? ? nil : ACL.new(acl, security_descriptor), defaulted.read_char != 0 ] end def self.initialize_acl(acl_size) acl = FFI::MemoryPointer.new acl_size unless InitializeAcl(acl, acl_size, ACL_REVISION) Chef::ReservedNames::Win32::Error.raise! end ACL.new(acl) end def self.initialize_security_descriptor(revision = SECURITY_DESCRIPTOR_REVISION) security_descriptor = FFI::MemoryPointer.new SECURITY_DESCRIPTOR_MIN_LENGTH unless InitializeSecurityDescriptor(security_descriptor, revision) Chef::ReservedNames::Win32::Error.raise! end SecurityDescriptor.new(security_descriptor) end def self.is_valid_acl(acl) acl = acl.pointer if acl.respond_to?(:pointer) IsValidAcl(acl) != 0 end def self.is_valid_security_descriptor(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) IsValidSecurityDescriptor(security_descriptor) != 0 end def self.is_valid_sid(sid) sid = sid.pointer if sid.respond_to?(:pointer) IsValidSid(sid) != 0 end def self.lookup_account_name(name, system_name = nil) # Figure out how big the buffers need to be sid_size = FFI::Buffer.new(:long).write_long(0) referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0) system_name = system_name.to_wstring if system_name if LookupAccountNameW(system_name, name.to_wstring, nil, sid_size, nil, referenced_domain_name_size, nil) raise "Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountName, and got no error!" elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end sid = FFI::MemoryPointer.new :char, sid_size.read_long referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long*2) use = FFI::Buffer.new(:long).write_long(0) unless LookupAccountNameW(system_name, name.to_wstring, sid, sid_size, referenced_domain_name, referenced_domain_name_size, use) Chef::ReservedNames::Win32::Error.raise! end [ referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), SID.new(sid), use.read_long ] end def self.lookup_account_sid(sid, system_name = nil) sid = sid.pointer if sid.respond_to?(:pointer) # Figure out how big the buffer needs to be name_size = FFI::Buffer.new(:long).write_long(0) referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0) system_name = system_name.to_wstring if system_name if LookupAccountSidW(system_name, sid, nil, name_size, nil, referenced_domain_name_size, nil) raise "Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!" elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end name = FFI::MemoryPointer.new :char, (name_size.read_long*2) referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long*2) use = FFI::Buffer.new(:long).write_long(0) unless LookupAccountSidW(system_name, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use) Chef::ReservedNames::Win32::Error.raise! end [ referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long ] end def self.lookup_privilege_name(system_name, luid) system_name = system_name.to_wstring if system_name name_size = FFI::Buffer.new(:long).write_long(0) if LookupPrivilegeNameW(system_name, luid, nil, name_size) raise "Expected ERROR_INSUFFICIENT_BUFFER from LookupPrivilegeName, and got no error!" elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end name = FFI::MemoryPointer.new :char, (name_size.read_long*2) unless LookupPrivilegeNameW(system_name, luid, name, name_size) Chef::ReservedNames::Win32::Error.raise! end name.read_wstring(name_size.read_long) end def self.lookup_privilege_display_name(system_name, name) system_name = system_name.to_wstring if system_name display_name_size = FFI::Buffer.new(:long).write_long(0) language_id = FFI::Buffer.new(:long) if LookupPrivilegeDisplayNameW(system_name, name.to_wstring, nil, display_name_size, language_id) raise "Expected ERROR_INSUFFICIENT_BUFFER from LookupPrivilegeDisplayName, and got no error!" elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end display_name = FFI::MemoryPointer.new :char, (display_name_size.read_long*2) unless LookupPrivilegeDisplayNameW(system_name, name.to_wstring, display_name, display_name_size, language_id) Chef::ReservedNames::Win32::Error.raise! end [ display_name.read_wstring(display_name_size.read_long), language_id.read_long ] end def self.lookup_privilege_value(system_name, name) luid = FFI::Buffer.new(:uint64).write_uint64(0) system_name = system_name.to_wstring if system_name unless LookupPrivilegeValueW(system_name, name.to_wstring, luid) Win32::Error.raise! end luid.read_uint64 end def self.make_absolute_sd(security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) # Figure out buffer sizes absolute_sd_size = FFI::Buffer.new(:long).write_long(0) dacl_size = FFI::Buffer.new(:long).write_long(0) sacl_size = FFI::Buffer.new(:long).write_long(0) owner_size = FFI::Buffer.new(:long).write_long(0) group_size = FFI::Buffer.new(:long).write_long(0) if MakeAbsoluteSD(security_descriptor, nil, absolute_sd_size, nil, dacl_size, nil, sacl_size, nil, owner_size, nil, group_size) raise "Expected ERROR_INSUFFICIENT_BUFFER from MakeAbsoluteSD, and got no error!" elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER Chef::ReservedNames::Win32::Error.raise! end absolute_sd = FFI::MemoryPointer.new absolute_sd_size.read_long owner = FFI::MemoryPointer.new owner_size.read_long group = FFI::MemoryPointer.new group_size.read_long dacl = FFI::MemoryPointer.new dacl_size.read_long sacl = FFI::MemoryPointer.new sacl_size.read_long unless MakeAbsoluteSD(security_descriptor, absolute_sd, absolute_sd_size, dacl, dacl_size, sacl, sacl_size, owner, owner_size, group, group_size) Chef::ReservedNames::Win32::Error.raise! end [ SecurityDescriptor.new(absolute_sd), SID.new(owner), SID.new(group), ACL.new(dacl), ACL.new(sacl) ] end def self.open_process_token(process, desired_access) process = process.handle if process.respond_to?(:handle) process = process.handle if process.respond_to?(:handle) token = FFI::Buffer.new(:ulong) unless OpenProcessToken(process, desired_access, token) Chef::ReservedNames::Win32::Error.raise! end Token.new(Handle.new(token.read_ulong)) end def self.query_security_access_mask(security_information) result = FFI::Buffer.new(:long) QuerySecurityAccessMask(security_information, result) result.read_long end def self.set_file_security(path, security_information, security_descriptor) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) unless SetFileSecurityW(path.to_wstring, security_information, security_descriptor) Chef::ReservedNames::Win32::Error.raise! end end def self.set_named_security_info(path, type, args) owner = args[:owner] group = args[:group] dacl = args[:dacl] sacl = args[:sacl] owner = owner.pointer if owner && owner.respond_to?(:pointer) group = group.pointer if group && group.respond_to?(:pointer) dacl = dacl.pointer if dacl && dacl.respond_to?(:pointer) sacl = sacl.pointer if sacl && sacl.respond_to?(:pointer) # Determine the security_information flags security_information = 0 security_information |= OWNER_SECURITY_INFORMATION if args.has_key?(:owner) security_information |= GROUP_SECURITY_INFORMATION if args.has_key?(:group) security_information |= DACL_SECURITY_INFORMATION if args.has_key?(:dacl) security_information |= SACL_SECURITY_INFORMATION if args.has_key?(:sacl) if args.has_key?(:dacl_inherits) security_information |= (args[:dacl_inherits] ? UNPROTECTED_DACL_SECURITY_INFORMATION : PROTECTED_DACL_SECURITY_INFORMATION) end if args.has_key?(:sacl_inherits) security_information |= (args[:sacl_inherits] ? UNPROTECTED_SACL_SECURITY_INFORMATION : PROTECTED_SACL_SECURITY_INFORMATION) end hr = SetNamedSecurityInfoW(path.to_wstring, type, security_information, owner, group, dacl, sacl) if hr != ERROR_SUCCESS Chef::ReservedNames::Win32::Error.raise! end end def self.set_security_access_mask(security_information) result = FFI::Buffer.new(:long) SetSecurityAccessMask(security_information, result) result.read_long end def set_security_descriptor_dacl(security_descriptor, acl, defaulted = false, present = nil) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) acl = acl.pointer if acl.respond_to?(:pointer) present = !security_descriptor.null? if present == nil unless SetSecurityDescriptorDacl(security_descriptor, present, acl, defaulted) Chef::ReservedNames::Win32::Error.raise! end end def self.set_security_descriptor_group(security_descriptor, sid, defaulted = false) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless SetSecurityDescriptorGroup(security_descriptor, sid, defaulted) Chef::ReservedNames::Win32::Error.raise! end end def self.set_security_descriptor_owner(security_descriptor, sid, defaulted = false) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) sid = sid.pointer if sid.respond_to?(:pointer) unless SetSecurityDescriptorOwner(security_descriptor, sid, defaulted) Chef::ReservedNames::Win32::Error.raise! end end def self.set_security_descriptor_sacl(security_descriptor, acl, defaulted = false, present = nil) security_descriptor = security_descriptor.pointer if security_descriptor.respond_to?(:pointer) acl = acl.pointer if acl.respond_to?(:pointer) present = !security_descriptor.null? if present == nil unless SetSecurityDescriptorSacl(security_descriptor, present, acl, defaulted) Chef::ReservedNames::Win32::Error.raise! end end def self.with_privileges(*privilege_names) # Set privileges token = open_process_token(Chef::ReservedNames::Win32::Process.get_current_process, TOKEN_READ | TOKEN_ADJUST_PRIVILEGES) old_privileges = token.enable_privileges(*privilege_names) # Let the caller do their privileged stuff begin yield ensure # Set privileges back to what they were before token.adjust_privileges(old_privileges) end end # Checks if the caller has the admin privileges in their # security token def self.has_admin_privileges? if Chef::Platform.windows_server_2003? # Admin privileges do not exist on Windows Server 2003 true else process_token = open_process_token(Chef::ReservedNames::Win32::Process.get_current_process, TOKEN_READ) elevation_result = FFI::Buffer.new(:ulong) elevation_result_size = FFI::MemoryPointer.new(:uint32) success = GetTokenInformation(process_token.handle.handle, :TokenElevation, elevation_result, 4, elevation_result_size) # Assume process is not elevated if the call fails. # Process is elevated if the result is different than 0. success && (elevation_result.read_ulong != 0) end end end end end require 'chef/win32/security/ace' require 'chef/win32/security/acl' require 'chef/win32/security/securable_object' require 'chef/win32/security/security_descriptor' require 'chef/win32/security/sid' chef-12.3.0/lib/chef/win32/api/0000755000004100000410000000000012520074675015730 5ustar www-datawww-datachef-12.3.0/lib/chef/win32/api/system.rb0000644000004100000410000002447412520074675017614 0ustar www-datawww-data# # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module System extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### # http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx # Suite Masks # Microsoft BackOffice components are installed. VER_SUITE_BACKOFFICE = 0x00000004 # Windows Server 2003, Web Edition is installed. VER_SUITE_BLADE = 0x00000400 # Windows Server 2003, Compute Cluster Edition is installed. VER_SUITE_COMPUTE_SERVER = 0x00004000 # Windows Server 2008 Datacenter, Windows Server 2003, Datacenter Edition, or Windows 2000 Datacenter Server is installed. VER_SUITE_DATACENTER = 0x00000080 # Windows Server 2008 Enterprise, Windows Server 2003, Enterprise Edition, or Windows 2000 Advanced Server is installed. Refer to the Remarks section for more information about this bit flag. VER_SUITE_ENTERPRISE = 0x00000002 # Windows XP Embedded is installed. VER_SUITE_EMBEDDEDNT = 0x00000040 # Windows Vista Home Premium, Windows Vista Home Basic, or Windows XP Home Edition is installed. VER_SUITE_PERSONAL = 0x00000200 # Remote Desktop is supported, but only one interactive session is supported. This value is set unless the system is running in application server mode. VER_SUITE_SINGLEUSERTS = 0x00000100 # Microsoft Small Business Server was once installed on the system, but may have been upgraded to another version of Windows. Refer to the Remarks section for more information about this bit flag. VER_SUITE_SMALLBUSINESS = 0x00000001 # Microsoft Small Business Server is installed with the restrictive client license in force. Refer to the Remarks section for more information about this bit flag. VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x00000020 # Windows Storage Server 2003 R2 or Windows Storage Server 2003is installed. VER_SUITE_STORAGE_SERVER = 0x00002000 # Terminal Services is installed. This value is always set. # If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server mode. VER_SUITE_TERMINAL = 0x00000010 # Windows Home Server is installed. VER_SUITE_WH_SERVER = 0x00008000 # Product Type # The system is a domain controller and the operating system is Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server. VER_NT_DOMAIN_CONTROLLER = 0x0000002 # The operating system is Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server. # Note that a server that is also a domain controller is reported as VER_NT_DOMAIN_CONTROLLER, not VER_NT_SERVER. VER_NT_SERVER = 0x0000003 # The operating system is Windows 7, Windows Vista, Windows XP Professional, Windows XP Home Edition, or Windows 2000 Professional. VER_NT_WORKSTATION = 0x0000001 # Product Info # http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx PRODUCT_BUSINESS = 0x00000006 # Business PRODUCT_BUSINESS_N = 0x00000010 # Business N PRODUCT_CLUSTER_SERVER = 0x00000012 # HPC Edition PRODUCT_DATACENTER_SERVER = 0x00000008 # Server Datacenter (full installation) PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C # Server Datacenter (core installation) PRODUCT_DATACENTER_SERVER_CORE_V = 0x00000027 # Server Datacenter without Hyper-V (core installation) PRODUCT_DATACENTER_SERVER_V = 0x00000025 # Server Datacenter without Hyper-V (full installation) PRODUCT_ENTERPRISE = 0x00000004 # Enterprise PRODUCT_ENTERPRISE_E = 0x00000046 # Not supported PRODUCT_ENTERPRISE_N = 0x0000001B # Enterprise N PRODUCT_ENTERPRISE_SERVER = 0x0000000A # Server Enterprise (full installation) PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E # Server Enterprise (core installation) PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029 # Server Enterprise without Hyper-V (core installation) PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F # Server Enterprise for Itanium-based Systems PRODUCT_ENTERPRISE_SERVER_V = 0x00000026 # Server Enterprise without Hyper-V (full installation) PRODUCT_HOME_BASIC = 0x00000002 # Home Basic PRODUCT_HOME_BASIC_E = 0x00000043 # Not supported PRODUCT_HOME_BASIC_N = 0x00000005 # Home Basic N PRODUCT_HOME_PREMIUM = 0x00000003 # Home Premium PRODUCT_HOME_PREMIUM_E = 0x00000044 # Not supported PRODUCT_HOME_PREMIUM_N = 0x0000001A # Home Premium N PRODUCT_HYPERV = 0x0000002A # Microsoft Hyper-V Server PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E # Windows Essential Business Server Management Server PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020 # Windows Essential Business Server Messaging Server PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F # Windows Essential Business Server Security Server PRODUCT_PROFESSIONAL = 0x00000030 # Professional PRODUCT_PROFESSIONAL_E = 0x00000045 # Not supported PRODUCT_PROFESSIONAL_N = 0x00000031 # Professional N PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018 # Windows Server 2008 for Windows Essential Server Solutions PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023 # Windows Server 2008 without Hyper-V for Windows Essential Server Solutions PRODUCT_SERVER_FOUNDATION = 0x00000021 # Server Foundation PRODUCT_HOME_PREMIUM_SERVER = 0x00000022 # Windows Home Server 2011 PRODUCT_SB_SOLUTION_SERVER = 0x00000032 # Windows Small Business Server 2011 Essentials PRODUCT_HOME_SERVER = 0x00000013 # Windows Storage Server 2008 R2 Essentials PRODUCT_SMALLBUSINESS_SERVER = 0x00000009 # Windows Small Business Server PRODUCT_SOLUTION_EMBEDDEDSERVER = 0x00000038 # Windows MultiPoint Server PRODUCT_STANDARD_SERVER = 0x00000007 # Server Standard (full installation) PRODUCT_STANDARD_SERVER_CORE = 0x0000000D # Server Standard (core installation) PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028 # Server Standard without Hyper-V (core installation) PRODUCT_STANDARD_SERVER_V = 0x00000024 # Server Standard without Hyper-V (full installation) PRODUCT_STARTER = 0x0000000B # Starter PRODUCT_STARTER_E = 0x00000042 # Not supported PRODUCT_STARTER_N = 0x0000002F # Starter N PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017 # Storage Server Enterprise PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014 # Storage Server Express PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015 # Storage Server Standard PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016 # Storage Server Workgroup PRODUCT_UNDEFINED = 0x00000000 # An unknown product PRODUCT_ULTIMATE = 0x00000001 # Ultimate PRODUCT_ULTIMATE_E = 0x00000047 # Not supported PRODUCT_ULTIMATE_N = 0x0000001C # Ultimate N PRODUCT_WEB_SERVER = 0x00000011 # Web Server (full installation) PRODUCT_WEB_SERVER_CORE = 0x0000001D # Web Server (core installation) # GetSystemMetrics # The build number if the system is Windows Server 2003 R2; otherwise, 0. SM_SERVERR2 = 89 ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32', 'user32' class OSVERSIONINFOEX < FFI::Struct layout :dw_os_version_info_size, :DWORD, :dw_major_version, :DWORD, :dw_minor_version, :DWORD, :dw_build_number, :DWORD, :dw_platform_id, :DWORD, :sz_csd_version, [:BYTE, 256], :w_service_pack_major, :WORD, :w_service_pack_minor, :WORD, :w_suite_mask, :WORD, :w_product_type, :BYTE, :w_reserved, :BYTE end =begin BOOL WINAPI CloseHandle( __in HANDLE hObject ); =end safe_attach_function :CloseHandle, [ :HANDLE ], :BOOL =begin DWORD WINAPI GetVersion(void); =end safe_attach_function :GetVersion, [], :DWORD =begin BOOL WINAPI GetVersionEx( __inout LPOSVERSIONINFO lpVersionInfo ); =end safe_attach_function :GetVersionExW, [:pointer], :BOOL safe_attach_function :GetVersionExA, [:pointer], :BOOL =begin BOOL WINAPI GetProductInfo( __in DWORD dwOSMajorVersion, __in DWORD dwOSMinorVersion, __in DWORD dwSpMajorVersion, __in DWORD dwSpMinorVersion, __out PDWORD pdwReturnedProductType ); =end safe_attach_function :GetProductInfo, [:DWORD, :DWORD, :DWORD, :DWORD, :PDWORD], :BOOL =begin int WINAPI GetSystemMetrics( __in int nIndex ); =end safe_attach_function :GetSystemMetrics, [:int], :int =begin LRESULT WINAPI SendMessageTimeout( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam, _In_ UINT fuFlags, _In_ UINT uTimeout, _Out_opt_ PDWORD_PTR lpdwResult ); =end safe_attach_function :SendMessageTimeoutW, [:HWND, :UINT, :WPARAM, :LPARAM, :UINT, :UINT, :PDWORD_PTR], :LRESULT safe_attach_function :SendMessageTimeoutA, [:HWND, :UINT, :WPARAM, :LPARAM, :UINT, :UINT, :PDWORD_PTR], :LRESULT =begin DWORD WINAPI ExpandEnvironmentStrings( _In_ LPCTSTR lpSrc, _Out_opt_ LPTSTR lpDst, _In_ DWORD nSize ); =end safe_attach_function :ExpandEnvironmentStringsW, [:pointer, :pointer, :DWORD], :DWORD safe_attach_function :ExpandEnvironmentStringsA, [:pointer, :pointer, :DWORD], :DWORD end end end end chef-12.3.0/lib/chef/win32/api/installer.rb0000644000004100000410000001415112520074675020254 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/exceptions' require 'chef/win32/api' require 'chef/win32/error' require 'pathname' class Chef module ReservedNames::Win32 module API module Installer extend Chef::ReservedNames::Win32 extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### ############################################### # Win32 API Bindings ############################################### ffi_lib 'msi' =begin UINT MsiOpenPackage( _In_ LPCTSTR szPackagePath, _Out_ MSIHANDLE *hProduct ); =end safe_attach_function :msi_open_package, :MsiOpenPackageExA, [ :string, :int, :pointer ], :int =begin UINT MsiGetProductProperty( _In_ MSIHANDLE hProduct, _In_ LPCTSTR szProperty, _Out_ LPTSTR lpValueBuf, _Inout_ DWORD *pcchValueBuf ); =end safe_attach_function :msi_get_product_property, :MsiGetProductPropertyA, [ :pointer, :pointer, :pointer, :pointer ], :int =begin UINT MsiGetProductInfo( _In_ LPCTSTR szProduct, _In_ LPCTSTR szProperty, _Out_ LPTSTR lpValueBuf, _Inout_ DWORD *pcchValueBuf ); =end safe_attach_function :msi_get_product_info, :MsiGetProductInfoA, [ :pointer, :pointer, :pointer, :pointer ], :int =begin UINT MsiCloseHandle( _In_ MSIHANDLE hAny ); =end safe_attach_function :msi_close_handle, :MsiCloseHandle, [ :pointer ], :int ############################################### # Helpers ############################################### # Opens a Microsoft Installer (MSI) file from an absolute path and returns the specified property def get_product_property(package_path, property_name) pkg_ptr = open_package(package_path) buffer = 0.chr buffer_length = FFI::Buffer.new(:long).write_long(0) # Fetch the length of the property status = msi_get_product_property(pkg_ptr.read_pointer, property_name, buffer, buffer_length) # We expect error ERROR_MORE_DATA (234) here because we passed a buffer length of 0 if status != 234 msg = "msi_get_product_property: returned unknown error #{status} when retrieving #{property_name}: " msg << Chef::ReservedNames::Win32::Error.format_message(status) raise Chef::Exceptions::Package, msg end buffer_length = FFI::Buffer.new(:long).write_long(buffer_length.read_long + 1) buffer = 0.chr * buffer_length.read_long # Fetch the property status = msi_get_product_property(pkg_ptr.read_pointer, property_name, buffer, buffer_length) if status != 0 msg = "msi_get_product_property: returned unknown error #{status} when retrieving #{property_name}: " msg << Chef::ReservedNames::Win32::Error.format_message(status) raise Chef::Exceptions::Package, msg end msi_close_handle(pkg_ptr.read_pointer) return buffer.chomp(0.chr) end # Opens a Microsoft Installer (MSI) file from an absolute path and returns a pointer to a handle # Remember to close the handle with msi_close_handle() def open_package(package_path) # MsiOpenPackage expects a perfect absolute Windows path to the MSI raise ArgumentError, "Provided path '#{package_path}' must be an absolute path" unless Pathname.new(package_path).absolute? pkg_ptr = FFI::MemoryPointer.new(:pointer, 4) status = msi_open_package(package_path, 1, pkg_ptr) case status when 0 # success else raise Chef::Exceptions::Package, "msi_open_package: unexpected status #{status}: #{Chef::ReservedNames::Win32::Error.format_message(status)}" end return pkg_ptr end # All installed product_codes should have a VersionString # Returns a version if installed, nil if not installed def get_installed_version(product_code) version = 0.chr version_length = FFI::Buffer.new(:long).write_long(0) status = msi_get_product_info(product_code, "VersionString", version, version_length) return nil if status == 1605 # ERROR_UNKNOWN_PRODUCT (0x645) # We expect error ERROR_MORE_DATA (234) here because we passed a buffer length of 0 if status != 234 msg = "msi_get_product_info: product code '#{product_code}' returned unknown error #{status} when retrieving VersionString: " msg << Chef::ReservedNames::Win32::Error.format_message(status) raise Chef::Exceptions::Package, msg end # We could fetch the product version now that we know the variable length, but we don't need it here. version_length = FFI::Buffer.new(:long).write_long(version_length.read_long + 1) version = 0.chr * version_length.read_long status = msi_get_product_info(product_code, "VersionString", version, version_length) if status != 0 msg = "msi_get_product_info: product code '#{product_code}' returned unknown error #{status} when retrieving VersionString: " msg << Chef::ReservedNames::Win32::Error.format_message(status) raise Chef::Exceptions::Package, msg end version end end end end end chef-12.3.0/lib/chef/win32/api/process.rb0000644000004100000410000000253512520074675017740 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Process extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32' safe_attach_function :GetCurrentProcess, [], :HANDLE safe_attach_function :GetProcessHandleCount, [ :HANDLE, :LPDWORD ], :BOOL safe_attach_function :GetProcessId, [ :HANDLE ], :DWORD safe_attach_function :CloseHandle, [ :HANDLE ], :BOOL safe_attach_function :IsWow64Process, [ :HANDLE, :PBOOL ], :BOOL end end end end chef-12.3.0/lib/chef/win32/api/psapi.rb0000644000004100000410000000307212520074675017373 0ustar www-datawww-data# # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module PSAPI extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Bindings ############################################### class PROCESS_MEMORY_COUNTERS < FFI::Struct layout :cb, :DWORD, :PageFaultCount, :DWORD, :PeakWorkingSetSize, :SIZE_T, :WorkingSetSize, :SIZE_T, :QuotaPeakPagedPoolUsage, :SIZE_T, :QuotaPagedPoolUsage, :SIZE_T, :QuotaPeakNonPagedPoolUsage, :SIZE_T, :QuotaNonPagedPoolUsage, :SIZE_T, :PagefileUsage, :SIZE_T, :PeakPagefileUsage, :SIZE_T end ffi_lib 'psapi' safe_attach_function :GetProcessMemoryInfo, [ :HANDLE, :pointer, :DWORD ], :BOOL end end end end chef-12.3.0/lib/chef/win32/api/crypto.rb0000644000004100000410000000347412520074675017605 0ustar www-datawww-data# # Author:: Jay Mundrawala () # Copyright:: Copyright 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Crypto extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Bindings ############################################### ffi_lib 'Crypt32' CRYPTPROTECT_UI_FORBIDDEN = 0x1 CRYPTPROTECT_LOCAL_MACHINE = 0x4 CRYPTPROTECT_AUDIT = 0x10 class CRYPT_INTEGER_BLOB < FFI::Struct layout :cbData, :DWORD, # Count, in bytes, of data :pbData, :pointer # Pointer to data buffer def initialize(str=nil) super(nil) if str self[:pbData] = FFI::MemoryPointer.from_string(str) self[:cbData] = str.bytesize end end end safe_attach_function :CryptProtectData, [ :PDATA_BLOB, :LPCWSTR, :PDATA_BLOB, :pointer, :PCRYPTPROTECT_PROMPTSTRUCT, :DWORD, :PDATA_BLOB ], :BOOL end end end end chef-12.3.0/lib/chef/win32/api/memory.rb0000644000004100000410000000565712520074675017602 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Memory extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### LMEM_FIXED = 0x0000 LMEM_MOVEABLE = 0x0002 LMEM_NOCOMPACT = 0x0010 LMEM_NODISCARD = 0x0020 LMEM_ZEROINIT = 0x0040 LMEM_MODIFY = 0x0080 LMEM_DISCARDABLE = 0x0F00 LMEM_VALID_FLAGS = 0x0F72 LMEM_INVALID_HANDLE = 0x8000 LHND = LMEM_MOVEABLE | LMEM_ZEROINIT LPTR = LMEM_FIXED | LMEM_ZEROINIT NONZEROLHND = LMEM_MOVEABLE NONZEROLPTR = LMEM_FIXED LMEM_DISCARDED = 0x4000 LMEM_LOCKCOUNT = 0x00FF ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32' =begin HLOCAL WINAPI LocalAlloc( __in UINT uFlags, __in SIZE_T uBytes ); =end safe_attach_function :LocalAlloc, [ :UINT, :SIZE_T ], :pointer =begin UINT WINAPI LocalFlags( __in HLOCAL hMem ); =end safe_attach_function :LocalFlags, [ :pointer ], :UINT =begin HLOCAL WINAPI LocalFree( __in HLOCAL hMem ); =end safe_attach_function :LocalFree, [ :pointer ], :pointer =begin HLOCAL WINAPI LocalReAlloc( __in HLOCAL hMem, __in SIZE_T uBytes, __in UINT uFlags ); =end safe_attach_function :LocalReAlloc, [ :pointer, :SIZE_T, :UINT ], :pointer =begin UINT WINAPI LocalSize( __in HLOCAL hMem ); =end safe_attach_function :LocalSize, [ :pointer ], :SIZE_T ############################################### # FFI API Bindings ############################################### ffi_lib FFI::Library::LIBC safe_attach_function :malloc, [:size_t], :pointer safe_attach_function :calloc, [:size_t], :pointer safe_attach_function :realloc, [:pointer, :size_t], :pointer safe_attach_function :free, [:pointer], :void safe_attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer end end end end chef-12.3.0/lib/chef/win32/api/security.rb0000644000004100000410000004407712520074675020140 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Security extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### # ACE_HEADER AceType ACCESS_MIN_MS_ACE_TYPE = 0x0 ACCESS_ALLOWED_ACE_TYPE = 0x0 ACCESS_DENIED_ACE_TYPE = 0x1 SYSTEM_AUDIT_ACE_TYPE = 0x2 SYSTEM_ALARM_ACE_TYPE = 0x3 ACCESS_MAX_MS_V2_ACE_TYPE = 0x3 ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 0x4 ACCESS_MAX_MS_V3_ACE_TYPE = 0x4 ACCESS_MIN_MS_OBJECT_ACE_TYPE = 0x5 ACCESS_ALLOWED_OBJECT_ACE_TYPE = 0x5 ACCESS_DENIED_OBJECT_ACE_TYPE = 0x6 SYSTEM_AUDIT_OBJECT_ACE_TYPE = 0x7 SYSTEM_ALARM_OBJECT_ACE_TYPE = 0x8 ACCESS_MAX_MS_OBJECT_ACE_TYPE = 0x8 ACCESS_MAX_MS_V4_ACE_TYPE = 0x8 ACCESS_MAX_MS_ACE_TYPE = 0x8 ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 0x9 ACCESS_DENIED_CALLBACK_ACE_TYPE = 0xA ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 0xB ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 0xC SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 0xD SYSTEM_ALARM_CALLBACK_ACE_TYPE = 0xE SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 0xF SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 0x10 SYSTEM_MANDATORY_LABEL_ACE_TYPE = 0x11 ACCESS_MAX_MS_V5_ACE_TYPE = 0x11 # ACE_HEADER AceFlags OBJECT_INHERIT_ACE = 0x1 CONTAINER_INHERIT_ACE = 0x2 NO_PROPAGATE_INHERIT_ACE = 0x4 INHERIT_ONLY_ACE = 0x8 INHERITED_ACE = 0x10 VALID_INHERIT_FLAGS = 0x1F SUCCESSFUL_ACCESS_ACE_FLAG = 0x40 FAILED_ACCESS_ACE_FLAG = 0x80 # SECURITY_INFORMATION flags (DWORD) OWNER_SECURITY_INFORMATION = 0x01 GROUP_SECURITY_INFORMATION = 0x02 DACL_SECURITY_INFORMATION = 0x04 SACL_SECURITY_INFORMATION = 0x08 LABEL_SECURITY_INFORMATION = 0x10 UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000 UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000 PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 # SECURITY_DESCRIPTOR_REVISION SECURITY_DESCRIPTOR_REVISION = 1 SECURITY_DESCRIPTOR_REVISION1 = 1 # SECURITY_DESCRIPTOR_CONTROL SE_OWNER_DEFAULTED = 0x0001 SE_GROUP_DEFAULTED = 0x0002 SE_DACL_PRESENT = 0x0004 SE_DACL_DEFAULTED = 0x0008 SE_SACL_PRESENT = 0x0010 SE_SACL_DEFAULTED = 0x0020 SE_DACL_AUTO_INHERIT_REQ = 0x0100 SE_SACL_AUTO_INHERIT_REQ = 0x0200 SE_DACL_AUTO_INHERITED = 0x0400 SE_SACL_AUTO_INHERITED = 0x0800 SE_DACL_PROTECTED = 0x1000 SE_SACL_PROTECTED = 0x2000 SE_RM_CONTROL_VALID = 0x4000 SE_SELF_RELATIVE = 0x8000 # ACCESS_RIGHTS_MASK # Generic Access Rights GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE = 0x20000000 GENERIC_ALL = 0x10000000 # Standard Access Rights DELETE = 0x00010000 READ_CONTROL = 0x00020000 WRITE_DAC = 0x00040000 WRITE_OWNER = 0x00080000 SYNCHRONIZE = 0x00100000 STANDARD_RIGHTS_REQUIRED = 0x000F0000 STANDARD_RIGHTS_READ = READ_CONTROL STANDARD_RIGHTS_WRITE = READ_CONTROL STANDARD_RIGHTS_EXECUTE = READ_CONTROL STANDARD_RIGHTS_ALL = 0x001F0000 SPECIFIC_RIGHTS_ALL = 0x0000FFFF # Access System Security Right ACCESS_SYSTEM_SECURITY = 0x01000000 # File/Directory Specific Rights FILE_READ_DATA = 0x0001 FILE_LIST_DIRECTORY = 0x0001 FILE_WRITE_DATA = 0x0002 FILE_ADD_FILE = 0x0002 FILE_APPEND_DATA = 0x0004 FILE_ADD_SUBDIRECTORY = 0x0004 FILE_CREATE_PIPE_INSTANCE = 0x0004 FILE_READ_EA = 0x0008 FILE_WRITE_EA = 0x0010 FILE_EXECUTE = 0x0020 FILE_TRAVERSE = 0x0020 FILE_DELETE_CHILD = 0x0040 FILE_READ_ATTRIBUTES = 0x0080 FILE_WRITE_ATTRIBUTES = 0x0100 FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE # Access Token Rights (for OpenProcessToken) # Access Rights for Access-Token Objects (used in OpenProcessToken) TOKEN_ASSIGN_PRIMARY = 0x0001 TOKEN_DUPLICATE = 0x0002 TOKEN_IMPERSONATE = 0x0004 TOKEN_QUERY = 0x0008 TOKEN_QUERY_SOURCE = 0x0010 TOKEN_ADJUST_PRIVILEGES = 0x0020 TOKEN_ADJUST_GROUPS = 0x0040 TOKEN_ADJUST_DEFAULT = 0x0080 TOKEN_ADJUST_SESSIONID = 0x0100 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY) TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID) # AdjustTokenPrivileges SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001 SE_PRIVILEGE_ENABLED = 0x00000002 SE_PRIVILEGE_REMOVED = 0X00000004 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000 SE_PRIVILEGE_VALID_ATTRIBUTES = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED | SE_PRIVILEGE_USED_FOR_ACCESS # Minimum size of a SECURITY_DESCRIPTOR. TODO: this is probably platform dependent. # Make it work on 64 bit. SECURITY_DESCRIPTOR_MIN_LENGTH = 20 # ACL revisions ACL_REVISION = 2 ACL_REVISION_DS = 4 ACL_REVISION1 = 1 ACL_REVISION2 = 2 ACL_REVISION3 = 3 ACL_REVISION4 = 4 MIN_ACL_REVISION = ACL_REVISION2 MAX_ACL_REVISION = ACL_REVISION4 MAXDWORD = 0xffffffff ############################################### # Win32 API Bindings ############################################### SE_OBJECT_TYPE = enum :SE_OBJECT_TYPE, [ :SE_UNKNOWN_OBJECT_TYPE, :SE_FILE_OBJECT, :SE_SERVICE, :SE_PRINTER, :SE_REGISTRY_KEY, :SE_LMSHARE, :SE_KERNEL_OBJECT, :SE_WINDOW_OBJECT, :SE_DS_OBJECT, :SE_DS_OBJECT_ALL, :SE_PROVIDER_DEFINED_OBJECT, :SE_WMIGUID_OBJECT, :SE_REGISTRY_WOW64_32KEY ] SID_NAME_USE = enum :SID_NAME_USE, [ :SidTypeUser, 1, :SidTypeGroup, :SidTypeDomain, :SidTypeAlias, :SidTypeWellKnownGroup, :SidTypeDeletedAccount, :SidTypeInvalid, :SidTypeUnknown, :SidTypeComputer, :SidTypeLabel ] TOKEN_INFORMATION_CLASS = enum :TOKEN_INFORMATION_CLASS, [ :TokenUser, 1, :TokenGroups, :TokenPrivileges, :TokenOwner, :TokenPrimaryGroup, :TokenDefaultDacl, :TokenSource, :TokenType, :TokenImpersonationLevel, :TokenStatistics, :TokenRestrictedSids, :TokenSessionId, :TokenGroupsAndPrivileges, :TokenSessionReference, :TokenSandBoxInert, :TokenAuditPolicy, :TokenOrigin, :TokenElevationType, :TokenLinkedToken, :TokenElevation, :TokenHasRestrictions, :TokenAccessInformation, :TokenVirtualizationAllowed, :TokenVirtualizationEnabled, :TokenIntegrityLevel, :TokenUIAccess, :TokenMandatoryPolicy, :TokenLogonSid, :TokenIsAppContainer, :TokenCapabilities, :TokenAppContainerSid, :TokenAppContainerNumber, :TokenUserClaimAttributes, :TokenDeviceClaimAttributes, :TokenRestrictedUserClaimAttributes, :TokenRestrictedDeviceClaimAttributes, :TokenDeviceGroups, :TokenRestrictedDeviceGroups, :TokenSecurityAttributes, :TokenIsRestricted, :MaxTokenInfoClass ] # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572%28v=vs.85%29.aspx SECURITY_IMPERSONATION_LEVEL = enum :SECURITY_IMPERSONATION_LEVEL, [ :SecurityAnonymous, :SecurityIdentification, :SecurityImpersonation, :SecurityDelegation ] # SECURITY_DESCRIPTOR is an opaque structure whose contents can vary. Pass the # pointer around and free it with LocalFree. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379561(v=vs.85).aspx # SID is an opaque structure. Pass the pointer around. # ACL type is a header with some information, followed by an array of ACEs # http://msdn.microsoft.com/en-us/library/windows/desktop/aa374931(v=VS.85).aspx class ACLStruct < FFI::Struct layout :AclRevision, :uchar, :Sbzl, :uchar, :AclSize, :ushort, :AceCount, :ushort, :Sbz2, :ushort end class ACE_HEADER < FFI::Struct layout :AceType, :uchar, :AceFlags, :uchar, :AceSize, :ushort end class ACE_WITH_MASK_AND_SID < FFI::Struct layout :AceType, :uchar, :AceFlags, :uchar, :AceSize, :ushort, :Mask, :uint32, :SidStart, :uint32 # The AceTypes this structure supports def self.supports?(ace_type) [ ACCESS_ALLOWED_ACE_TYPE, ACCESS_DENIED_ACE_TYPE, SYSTEM_AUDIT_ACE_TYPE, SYSTEM_ALARM_ACE_TYPE ].include?(ace_type) end end class LUID < FFI::Struct layout :LowPart, :DWORD, :HighPart, :LONG end class LUID_AND_ATTRIBUTES < FFI::Struct layout :Luid, LUID, :Attributes, :DWORD end class GENERIC_MAPPING < FFI::Struct layout :GenericRead, :DWORD, :GenericWrite, :DWORD, :GenericExecute, :DWORD, :GenericAll, :DWORD end class PRIVILEGE_SET < FFI::Struct layout :PrivilegeCount, :DWORD, :Control, :DWORD, :Privilege, [LUID_AND_ATTRIBUTES, 1] end class TOKEN_PRIVILEGES < FFI::Struct layout :PrivilegeCount, :DWORD, :Privileges, LUID_AND_ATTRIBUTES def self.size_with_privileges(num_privileges) offset_of(:Privileges) + LUID_AND_ATTRIBUTES.size*num_privileges end def size_with_privileges TOKEN_PRIVILEGES.size_with_privileges(self[:PrivilegeCount]) end def privilege(index) LUID_AND_ATTRIBUTES.new(pointer + offset_of(:Privileges) + (index * LUID_AND_ATTRIBUTES.size)) end end ffi_lib "advapi32" safe_attach_function :AccessCheck, [:pointer, :HANDLE, :DWORD, :pointer, :pointer, :pointer, :pointer, :pointer], :BOOL safe_attach_function :AddAce, [ :pointer, :DWORD, :DWORD, :LPVOID, :DWORD ], :BOOL safe_attach_function :AddAccessAllowedAce, [ :pointer, :DWORD, :DWORD, :pointer ], :BOOL safe_attach_function :AddAccessAllowedAceEx, [ :pointer, :DWORD, :DWORD, :DWORD, :pointer ], :BOOL safe_attach_function :AddAccessDeniedAce, [ :pointer, :DWORD, :DWORD, :pointer ], :BOOL safe_attach_function :AddAccessDeniedAceEx, [ :pointer, :DWORD, :DWORD, :DWORD, :pointer ], :BOOL safe_attach_function :AdjustTokenPrivileges, [ :HANDLE, :BOOL, :pointer, :DWORD, :pointer, :PDWORD ], :BOOL safe_attach_function :ConvertSidToStringSidA, [ :pointer, :pointer ], :BOOL safe_attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :BOOL safe_attach_function :DeleteAce, [ :pointer, :DWORD ], :BOOL safe_attach_function :DuplicateToken, [:HANDLE, :SECURITY_IMPERSONATION_LEVEL, :PHANDLE], :BOOL safe_attach_function :EqualSid, [ :pointer, :pointer ], :BOOL safe_attach_function :FreeSid, [ :pointer ], :pointer safe_attach_function :GetAce, [ :pointer, :DWORD, :pointer ], :BOOL safe_attach_function :GetFileSecurityW, [:LPCWSTR, :DWORD, :pointer, :DWORD, :pointer], :BOOL safe_attach_function :GetLengthSid, [ :pointer ], :DWORD safe_attach_function :GetNamedSecurityInfoW, [ :LPWSTR, :SE_OBJECT_TYPE, :DWORD, :pointer, :pointer, :pointer, :pointer, :pointer ], :DWORD safe_attach_function :GetSecurityDescriptorControl, [ :pointer, :PWORD, :LPDWORD], :BOOL safe_attach_function :GetSecurityDescriptorDacl, [ :pointer, :LPBOOL, :pointer, :LPBOOL ], :BOOL safe_attach_function :GetSecurityDescriptorGroup, [ :pointer, :pointer, :LPBOOL], :BOOL safe_attach_function :GetSecurityDescriptorOwner, [ :pointer, :pointer, :LPBOOL], :BOOL safe_attach_function :GetSecurityDescriptorSacl, [ :pointer, :LPBOOL, :pointer, :LPBOOL ], :BOOL safe_attach_function :InitializeAcl, [ :pointer, :DWORD, :DWORD ], :BOOL safe_attach_function :InitializeSecurityDescriptor, [ :pointer, :DWORD ], :BOOL safe_attach_function :IsValidAcl, [ :pointer ], :BOOL safe_attach_function :IsValidSecurityDescriptor, [ :pointer ], :BOOL safe_attach_function :IsValidSid, [ :pointer ], :BOOL safe_attach_function :LookupAccountNameW, [ :LPCWSTR, :LPCWSTR, :pointer, :LPDWORD, :LPWSTR, :LPDWORD, :pointer ], :BOOL safe_attach_function :LookupAccountSidW, [ :LPCWSTR, :pointer, :LPWSTR, :LPDWORD, :LPWSTR, :LPDWORD, :pointer ], :BOOL safe_attach_function :LookupPrivilegeNameW, [ :LPCWSTR, :PLUID, :LPWSTR, :LPDWORD ], :BOOL safe_attach_function :LookupPrivilegeDisplayNameW, [ :LPCWSTR, :LPCWSTR, :LPWSTR, :LPDWORD, :LPDWORD ], :BOOL safe_attach_function :LookupPrivilegeValueW, [ :LPCWSTR, :LPCWSTR, :PLUID ], :BOOL safe_attach_function :MakeAbsoluteSD, [ :pointer, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD, :pointer, :LPDWORD], :BOOL safe_attach_function :MapGenericMask, [ :PDWORD, :PGENERICMAPPING ], :void safe_attach_function :OpenProcessToken, [ :HANDLE, :DWORD, :PHANDLE ], :BOOL safe_attach_function :QuerySecurityAccessMask, [ :DWORD, :LPDWORD ], :void safe_attach_function :SetFileSecurityW, [ :LPWSTR, :DWORD, :pointer ], :BOOL safe_attach_function :SetNamedSecurityInfoW, [ :LPWSTR, :SE_OBJECT_TYPE, :DWORD, :pointer, :pointer, :pointer, :pointer ], :DWORD safe_attach_function :SetSecurityAccessMask, [ :DWORD, :LPDWORD ], :void safe_attach_function :SetSecurityDescriptorDacl, [ :pointer, :BOOL, :pointer, :BOOL ], :BOOL safe_attach_function :SetSecurityDescriptorGroup, [ :pointer, :pointer, :BOOL ], :BOOL safe_attach_function :SetSecurityDescriptorOwner, [ :pointer, :pointer, :BOOL ], :BOOL safe_attach_function :SetSecurityDescriptorSacl, [ :pointer, :BOOL, :pointer, :BOOL ], :BOOL safe_attach_function :GetTokenInformation, [ :HANDLE, :TOKEN_INFORMATION_CLASS, :pointer, :DWORD, :PDWORD ], :BOOL end end end end chef-12.3.0/lib/chef/win32/api/synchronization.rb0000644000004100000410000000510312520074675021515 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Synchronization extend Chef::ReservedNames::Win32::API ffi_lib 'kernel32' # Constant synchronization functions use to indicate wait # forever. INFINITE = 0xFFFFFFFF # Return codes # http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx WAIT_FAILED = 0xFFFFFFFF WAIT_TIMEOUT = 0x00000102 WAIT_OBJECT_0 = 0x00000000 WAIT_ABANDONED = 0x00000080 # Security and access rights for synchronization objects # http://msdn.microsoft.com/en-us/library/windows/desktop/ms686670(v=vs.85).aspx DELETE = 0x00010000 READ_CONTROL = 0x00020000 SYNCHRONIZE = 0x00100000 WRITE_DAC = 0x00040000 WRITE_OWNER = 0x00080000 # Mutex specific rights MUTEX_ALL_ACCESS = 0x001F0001 MUTEX_MODIFY_STATE = 0x00000001 =begin HANDLE WINAPI CreateMutex( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, _In_opt_ LPCTSTR lpName ); =end safe_attach_function :CreateMutexW, [ :LPSECURITY_ATTRIBUTES, :BOOL, :LPCTSTR ], :HANDLE safe_attach_function :CreateMutexA, [ :LPSECURITY_ATTRIBUTES, :BOOL, :LPCTSTR ], :HANDLE =begin DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, _In_ DWORD dwMilliseconds ); =end safe_attach_function :WaitForSingleObject, [ :HANDLE, :DWORD ], :DWORD =begin BOOL WINAPI ReleaseMutex( _In_ HANDLE hMutex ); =end safe_attach_function :ReleaseMutex, [ :HANDLE ], :BOOL =begin HANDLE WINAPI OpenMutex( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName ); =end safe_attach_function :OpenMutexW, [ :DWORD, :BOOL, :LPCTSTR ], :HANDLE safe_attach_function :OpenMutexA, [ :DWORD, :BOOL, :LPCTSTR ], :HANDLE end end end end chef-12.3.0/lib/chef/win32/api/file.rb0000644000004100000410000004753612520074675017213 0ustar www-datawww-data# # Author:: Seth Chisamore () # Author:: Mark Mzyk () # Copyright:: Copyright 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 'chef/win32/api' require 'chef/win32/api/security' require 'chef/win32/api/system' class Chef module ReservedNames::Win32 module API module File extend Chef::ReservedNames::Win32::API include Chef::ReservedNames::Win32::API::Security include Chef::ReservedNames::Win32::API::System ############################################### # Win32 API Constants ############################################### FILE_ATTRIBUTE_READONLY = 0x00000001 FILE_ATTRIBUTE_HIDDEN = 0x00000002 FILE_ATTRIBUTE_SYSTEM = 0x00000004 FILE_ATTRIBUTE_DIRECTORY = 0x00000010 FILE_ATTRIBUTE_ARCHIVE = 0x00000020 FILE_ATTRIBUTE_DEVICE = 0x00000040 FILE_ATTRIBUTE_NORMAL = 0x00000080 FILE_ATTRIBUTE_TEMPORARY = 0x00000100 FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200 FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400 FILE_ATTRIBUTE_COMPRESSED = 0x00000800 FILE_ATTRIBUTE_OFFLINE = 0x00001000 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000 FILE_ATTRIBUTE_ENCRYPTED = 0x00004000 FILE_ATTRIBUTE_VIRTUAL = 0x00010000 INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF FILE_FLAG_WRITE_THROUGH = 0x80000000 FILE_FLAG_OVERLAPPED = 0x40000000 FILE_FLAG_NO_BUFFERING = 0x20000000 FILE_FLAG_RANDOM_ACCESS = 0x10000000 FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000 FILE_FLAG_DELETE_ON_CLOSE = 0x04000000 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 FILE_FLAG_POSIX_SEMANTICS = 0x01000000 FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 FILE_FLAG_OPEN_NO_RECALL = 0x00100000 FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000 INVALID_HANDLE_VALUE = 0xFFFFFFFF MAX_PATH = 260 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 FILE_NAME_NORMALIZED = 0x0 FILE_NAME_OPENED = 0x8 # TODO add the rest of these CONSTS FILE_SHARE_READ = 0x00000001 OPEN_EXISTING = 3 # DeviceIoControl control codes # ----------------------------- FILE_DEVICE_BEEP = 0x00000001 FILE_DEVICE_CD_ROM = 0x00000002 FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003 FILE_DEVICE_CONTROLLER = 0x00000004 FILE_DEVICE_DATALINK = 0x00000005 FILE_DEVICE_DFS = 0x00000006 FILE_DEVICE_DISK = 0x00000007 FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008 FILE_DEVICE_FILE_SYSTEM = 0x00000009 FILE_DEVICE_INPORT_PORT = 0x0000000a FILE_DEVICE_KEYBOARD = 0x0000000b FILE_DEVICE_MAILSLOT = 0x0000000c FILE_DEVICE_MIDI_IN = 0x0000000d FILE_DEVICE_MIDI_OUT = 0x0000000e FILE_DEVICE_MOUSE = 0x0000000f FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010 FILE_DEVICE_NAMED_PIPE = 0x00000011 FILE_DEVICE_NETWORK = 0x00000012 FILE_DEVICE_NETWORK_BROWSER = 0x00000013 FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014 FILE_DEVICE_NULL = 0x00000015 FILE_DEVICE_PARALLEL_PORT = 0x00000016 FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017 FILE_DEVICE_PRINTER = 0x00000018 FILE_DEVICE_SCANNER = 0x00000019 FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001a FILE_DEVICE_SERIAL_PORT = 0x0000001b FILE_DEVICE_SCREEN = 0x0000001c FILE_DEVICE_SOUND = 0x0000001d FILE_DEVICE_STREAMS = 0x0000001e FILE_DEVICE_TAPE = 0x0000001f FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020 FILE_DEVICE_TRANSPORT = 0x00000021 FILE_DEVICE_UNKNOWN = 0x00000022 FILE_DEVICE_VIDEO = 0x00000023 FILE_DEVICE_VIRTUAL_DISK = 0x00000024 FILE_DEVICE_WAVE_IN = 0x00000025 FILE_DEVICE_WAVE_OUT = 0x00000026 FILE_DEVICE_8042_PORT = 0x00000027 FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028 FILE_DEVICE_BATTERY = 0x00000029 FILE_DEVICE_BUS_EXTENDER = 0x0000002a FILE_DEVICE_MODEM = 0x0000002b FILE_DEVICE_VDM = 0x0000002c FILE_DEVICE_MASS_STORAGE = 0x0000002d FILE_DEVICE_SMB = 0x0000002e FILE_DEVICE_KS = 0x0000002f FILE_DEVICE_CHANGER = 0x00000030 FILE_DEVICE_SMARTCARD = 0x00000031 FILE_DEVICE_ACPI = 0x00000032 FILE_DEVICE_DVD = 0x00000033 FILE_DEVICE_FULLSCREEN_VIDEO = 0x00000034 FILE_DEVICE_DFS_FILE_SYSTEM = 0x00000035 FILE_DEVICE_DFS_VOLUME = 0x00000036 FILE_DEVICE_SERENUM = 0x00000037 FILE_DEVICE_TERMSRV = 0x00000038 FILE_DEVICE_KSEC = 0x00000039 FILE_DEVICE_FIPS = 0x0000003A FILE_DEVICE_INFINIBAND = 0x0000003B FILE_DEVICE_VMBUS = 0x0000003E FILE_DEVICE_CRYPT_PROVIDER = 0x0000003F FILE_DEVICE_WPD = 0x00000040 FILE_DEVICE_BLUETOOTH = 0x00000041 FILE_DEVICE_MT_COMPOSITE = 0x00000042 FILE_DEVICE_MT_TRANSPORT = 0x00000043 FILE_DEVICE_BIOMETRIC = 0x00000044 FILE_DEVICE_PMI = 0x00000045 # Methods METHOD_BUFFERED = 0 METHOD_IN_DIRECT = 1 METHOD_OUT_DIRECT = 2 METHOD_NEITHER = 3 METHOD_DIRECT_TO_HARDWARE = METHOD_IN_DIRECT METHOD_DIRECT_FROM_HARDWARE = METHOD_OUT_DIRECT # Access FILE_ANY_ACCESS = 0 FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS FILE_READ_ACCESS = 0x0001 FILE_WRITE_ACCESS = 0x0002 def self.CTL_CODE( device_type, function, method, access ) (device_type << 16) | (access << 14) | (function << 2) | method end FSCTL_GET_REPARSE_POINT = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) # Reparse point tags IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 IO_REPARSE_TAG_HSM = 0xC0000004 IO_REPARSE_TAG_HSM2 = 0x80000006 IO_REPARSE_TAG_SIS = 0x80000007 IO_REPARSE_TAG_WIM = 0x80000008 IO_REPARSE_TAG_CSV = 0x80000009 IO_REPARSE_TAG_DFS = 0x8000000A IO_REPARSE_TAG_SYMLINK = 0xA000000C IO_REPARSE_TAG_DFSR = 0x80000012 MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16*1024 ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32' =begin typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, *PFILETIME; =end class FILETIME < FFI::Struct layout :dw_low_date_time, :DWORD, :dw_high_date_time, :DWORD end =begin typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; =end class SECURITY_ATTRIBUTES < FFI::Struct layout :n_length, :DWORD, :lp_security_descriptor, :LPVOID, :b_inherit_handle, :DWORD end =begin typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; TCHAR cFileName[MAX_PATH]; TCHAR cAlternateFileName[14]; } WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA; =end class WIN32_FIND_DATA < FFI::Struct layout :dw_file_attributes, :DWORD, :ft_creation_time, FILETIME, :ft_last_access_time, FILETIME, :ft_last_write_time, FILETIME, :n_file_size_high, :DWORD, :n_file_size_low, :DWORD, :dw_reserved_0, :DWORD, :dw_reserved_1, :DWORD, :c_file_name, [:BYTE, MAX_PATH*2], :c_alternate_file_name, [:BYTE, 14] end =begin typedef struct _BY_HANDLE_FILE_INFORMATION { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD dwVolumeSerialNumber; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD nNumberOfLinks; DWORD nFileIndexHigh; DWORD nFileIndexLow; } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION; =end class BY_HANDLE_FILE_INFORMATION < FFI::Struct layout :dw_file_attributes, :DWORD, :ft_creation_time, FILETIME, :ft_last_access_time, FILETIME, :ft_last_write_time, FILETIME, :dw_volume_serial_number, :DWORD, :n_file_size_high, :DWORD, :n_file_size_low, :DWORD, :n_number_of_links, :DWORD, :n_file_index_high, :DWORD, :n_file_index_low, :DWORD end =begin typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; =end class REPARSE_DATA_BUFFER_SYMBOLIC_LINK < FFI::Struct layout :SubstituteNameOffset, :ushort, :SubstituteNameLength, :ushort, :PrintNameOffset, :ushort, :PrintNameLength, :ushort, :Flags, :uint32, :PathBuffer, :ushort def substitute_name string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:SubstituteNameOffset] string_pointer.read_wstring(self[:SubstituteNameLength]/2) end def print_name string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:PrintNameOffset] string_pointer.read_wstring(self[:PrintNameLength]/2) end end class REPARSE_DATA_BUFFER_MOUNT_POINT < FFI::Struct layout :SubstituteNameOffset, :ushort, :SubstituteNameLength, :ushort, :PrintNameOffset, :ushort, :PrintNameLength, :ushort, :PathBuffer, :ushort def substitute_name string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:SubstituteNameOffset] string_pointer.read_wstring(self[:SubstituteNameLength]/2) end def print_name string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:PrintNameOffset] string_pointer.read_wstring(self[:PrintNameLength]/2) end end class REPARSE_DATA_BUFFER_GENERIC < FFI::Struct layout :DataBuffer, :uchar end class REPARSE_DATA_BUFFER_UNION < FFI::Union layout :SymbolicLinkReparseBuffer, REPARSE_DATA_BUFFER_SYMBOLIC_LINK, :MountPointReparseBuffer, REPARSE_DATA_BUFFER_MOUNT_POINT, :GenericReparseBuffer, REPARSE_DATA_BUFFER_GENERIC end class REPARSE_DATA_BUFFER < FFI::Struct layout :ReparseTag, :uint32, :ReparseDataLength, :ushort, :Reserved, :ushort, :ReparseBuffer, REPARSE_DATA_BUFFER_UNION def reparse_buffer if self[:ReparseTag] == IO_REPARSE_TAG_SYMLINK self[:ReparseBuffer][:SymbolicLinkReparseBuffer] elsif self[:ReparseTag] == IO_REPARSE_TAG_MOUNT_POINT self[:ReparseBuffer][:MountPointReparseBuffer] else self[:ReparseBuffer][:GenericReparseBuffer] end end end =begin HANDLE WINAPI CreateFile( __in LPCTSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile ); =end safe_attach_function :CreateFileW, [:LPCTSTR, :DWORD, :DWORD, :LPSECURITY_ATTRIBUTES, :DWORD, :DWORD, :pointer], :HANDLE =begin BOOL WINAPI FindClose( __inout HANDLE hFindFile ); =end safe_attach_function :FindClose, [:HANDLE], :BOOL =begin DWORD WINAPI GetFileAttributes( __in LPCTSTR lpFileName ); =end safe_attach_function :GetFileAttributesW, [:LPCWSTR], :DWORD =begin DWORD WINAPI GetFinalPathNameByHandle( __in HANDLE hFile, __out LPTSTR lpszFilePath, __in DWORD cchFilePath, __in DWORD dwFlags ); =end safe_attach_function :GetFinalPathNameByHandleW, [:HANDLE, :LPTSTR, :DWORD, :DWORD], :DWORD =begin BOOL WINAPI GetFileInformationByHandle( __in HANDLE hFile, __out LPBY_HANDLE_FILE_INFORMATION lpFileInformation ); =end safe_attach_function :GetFileInformationByHandle, [:HANDLE, :LPBY_HANDLE_FILE_INFORMATION], :BOOL =begin HANDLE WINAPI FindFirstFile( __in LPCTSTR lpFileName, __out LPWIN32_FIND_DATA lpFindFileData ); =end safe_attach_function :FindFirstFileW, [:LPCTSTR, :LPWIN32_FIND_DATA], :HANDLE =begin BOOL WINAPI CreateHardLink( __in LPCTSTR lpFileName, __in LPCTSTR lpExistingFileName, __reserved LPSECURITY_ATTRIBUTES lpSecurityAttributes ); =end safe_attach_function :CreateHardLinkW, [:LPCTSTR, :LPCTSTR, :LPSECURITY_ATTRIBUTES], :BOOLEAN =begin BOOLEAN WINAPI CreateSymbolicLink( __in LPTSTR lpSymlinkFileName, __in LPTSTR lpTargetFileName, __in DWORD dwFlags ); =end safe_attach_function :CreateSymbolicLinkW, [:LPTSTR, :LPTSTR, :DWORD], :BOOLEAN =begin DWORD WINAPI GetLongPathName( __in LPCTSTR lpszShortPath, __out LPTSTR lpszLongPath, __in DWORD cchBuffer ); =end safe_attach_function :GetLongPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD =begin DWORD WINAPI GetShortPathName( __in LPCTSTR lpszLongPath, __out LPTSTR lpszShortPath, __in DWORD cchBuffer ); =end safe_attach_function :GetShortPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD =begin BOOL WINAPI DeviceIoControl( __in HANDLE hDevice, __in DWORD dwIoControlCode, __in_opt LPVOID lpInBuffer, __in DWORD nInBufferSize, __out_opt LPVOID lpOutBuffer, __in DWORD nOutBufferSize, __out_opt LPDWORD lpBytesReturned, __inout_opt LPOVERLAPPED lpOverlapped ); =end safe_attach_function :DeviceIoControl, [:HANDLE, :DWORD, :LPVOID, :DWORD, :LPVOID, :DWORD, :LPDWORD, :pointer], :BOOL ############################################### # Helpers ############################################### # takes the given path pre-pends "\\?\" and # UTF-16LE encodes it. Used to prepare paths # to be passed to the *W vesion of WinAPI File # functions. # This function is used by the "Link" resources where we need # preserve relative paths because symbolic links can actually # point to a relative path (relative to the link itself). def encode_path(path) (path_prepender << path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)).to_wstring end # Expands the path, prepends "\\?\" and UTF-16LE encodes it. # This function is used by the "File" resources where we need # convert relative paths to fully qualified paths. def canonical_encode_path(path) Chef::Util::PathHelper.canonical_path(path).to_wstring end def path_prepender "\\\\?\\" end # retrieves a file search handle and passes it # to +&block+ along with the find_data. also # ensures the handle is closed on exit of the block def file_search_handle(path, &block) begin # Workaround for CHEF-4419: # Make sure paths starting with "/" has a drive letter # assigned from the current working diretory. # Note: With CHEF-4427 this issue will be fixed with a # broader fix to map all the paths starting with "/" to # SYSTEM_DRIVE on windows. path = ::File.expand_path(path) if path.start_with? "/" path = canonical_encode_path(path) find_data = WIN32_FIND_DATA.new handle = FindFirstFileW(path, find_data) if handle == INVALID_HANDLE_VALUE Chef::ReservedNames::Win32::Error.raise! end block.call(handle, find_data) ensure FindClose(handle) if handle && handle != INVALID_HANDLE_VALUE end end # retrieves a file handle and passes it # to +&block+ along with the find_data. also # ensures the handle is closed on exit of the block def file_handle(path, &block) begin path = canonical_encode_path(path) handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil) if handle == INVALID_HANDLE_VALUE Chef::ReservedNames::Win32::Error.raise! end block.call(handle) ensure CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE end end def symlink_file_handle(path, &block) begin path = encode_path(path) handle = CreateFileW(path, FILE_READ_EA, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nil) if handle == INVALID_HANDLE_VALUE Chef::ReservedNames::Win32::Error.raise! end block.call(handle) ensure CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE end end def retrieve_file_info(file_name) file_information = nil file_handle(file_name) do |handle| file_information = BY_HANDLE_FILE_INFORMATION.new success = GetFileInformationByHandle(handle, file_information) if success == 0 Chef::ReservedNames::Win32::Error.raise! end end file_information end end end end end chef-12.3.0/lib/chef/win32/api/unicode.rb0000644000004100000410000001313712520074675017710 0ustar www-datawww-data# # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Unicode extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### CP_ACP = 0 CP_OEMCP = 1 CP_MACCP = 2 CP_THREAD_ACP = 3 CP_SYMBOL = 42 CP_UTF7 = 65000 CP_UTF8 = 65001 MB_PRECOMPOSED = 0x00000001 MB_COMPOSITE = 0x00000002 MB_USEGLYPHCHARS = 0x00000004 MB_ERR_INVALID_CHARS = 0x00000008 WC_COMPOSITECHECK = 0x00000200 WC_DISCARDNS = 0x00000010 WC_SEPCHARS = 0x00000020 WC_DEFAULTCHAR = 0x00000040 WC_NO_BEST_FIT_CHARS = 0x00000400 ANSI_CHARSET = 0 DEFAULT_CHARSET = 1 SYMBOL_CHARSET = 2 SHIFTJIS_CHARSET = 128 HANGEUL_CHARSET = 129 HANGUL_CHARSET = 129 GB2312_CHARSET = 134 CHINESEBIG5_CHARSET = 136 OEM_CHARSET = 255 JOHAB_CHARSET = 130 HEBREW_CHARSET = 177 ARABIC_CHARSET = 178 GREEK_CHARSET = 161 TURKISH_CHARSET = 162 VIETNAMESE_CHARSET = 163 THAI_CHARSET = 222 EASTEUROPE_CHARSET = 238 RUSSIAN_CHARSET = 204 IS_TEXT_UNICODE_ASCII16 = 0x0001 IS_TEXT_UNICODE_REVERSE_ASCII16 = 0x0010 IS_TEXT_UNICODE_STATISTICS = 0x0002 IS_TEXT_UNICODE_REVERSE_STATISTICS = 0x0020 IS_TEXT_UNICODE_CONTROLS = 0x0004 IS_TEXT_UNICODE_REVERSE_CONTROLS = 0x0040 IS_TEXT_UNICODE_SIGNATURE = 0x0008 IS_TEXT_UNICODE_REVERSE_SIGNATURE = 0x0080 IS_TEXT_UNICODE_ILLEGAL_CHARS = 0x0100 IS_TEXT_UNICODE_ODD_LENGTH = 0x0200 IS_TEXT_UNICODE_DBCS_LEADBYTE = 0x0400 IS_TEXT_UNICODE_NULL_BYTES = 0x1000 IS_TEXT_UNICODE_UNICODE_MASK = 0x000F IS_TEXT_UNICODE_REVERSE_MASK = 0x00F0 IS_TEXT_UNICODE_NOT_UNICODE_MASK = 0x0F00 IS_TEXT_UNICODE_NOT_ASCII_MASK = 0xF000 TCI_SRCCHARSET = 1 TCI_SRCCODEPAGE = 2 TCI_SRCFONTSIG = 3 TCI_SRCLOCALE = 0x100 ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32', 'advapi32' =begin BOOL IsTextUnicode( __in const VOID *lpv, __in int iSize, __inout LPINT lpiResult ); =end safe_attach_function :IsTextUnicode, [:pointer, :int, :LPINT], :BOOL =begin int MultiByteToWideChar( __in UINT CodePage, __in DWORD dwFlags, __in LPCSTR lpMultiByteStr, __in int cbMultiByte, __out LPWSTR lpWideCharStr, __in int cchWideChar ); =end safe_attach_function :MultiByteToWideChar, [:UINT, :DWORD, :LPCSTR, :int, :LPWSTR, :int], :int =begin int WideCharToMultiByte( __in UINT CodePage, __in DWORD dwFlags, __in LPCWSTR lpWideCharStr, __in int cchWideChar, __out LPSTR lpMultiByteStr, __in int cbMultiByte, __in LPCSTR lpDefaultChar, __out LPBOOL lpUsedDefaultChar ); =end safe_attach_function :WideCharToMultiByte, [:UINT, :DWORD, :LPCWSTR, :int, :LPSTR, :int, :LPCSTR, :LPBOOL], :int ############################################### # Helpers ############################################### def utf8_to_wide(ustring) # ensure it is actually UTF-8 # Ruby likes to mark binary data as ASCII-8BIT ustring = (ustring + "").force_encoding('UTF-8') if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8" # ensure we have the double-null termination Windows Wide likes ustring = ustring + "\000\000" if ustring[-1].chr != "\000" # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode ustring = begin if ustring.respond_to?(:encode) ustring.encode('UTF-16LE') else require 'iconv' Iconv.conv("UTF-16LE", "UTF-8", ustring) end end ustring end def wide_to_utf8(wstring) # ensure it is actually UTF-16LE # Ruby likes to mark binary data as ASCII-8BIT wstring = wstring.force_encoding('UTF-16LE') if wstring.respond_to?(:force_encoding) # encode it all as UTF-8 wstring = begin if wstring.respond_to?(:encode) wstring.encode('UTF-8') else require 'iconv' Iconv.conv("UTF-8", "UTF-16LE", wstring) end end # remove trailing CRLF and NULL characters wstring.strip! wstring end end end end end chef-12.3.0/lib/chef/win32/api/net.rb0000644000004100000410000000575712520074675017061 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Net extend Chef::ReservedNames::Win32::API FILTER_TEMP_DUPLICATE_ACCOUNT = 0x0001 FILTER_NORMAL_ACCOUNT = 0x0002 FILTER_INTERDOMAIN_TRUST_ACCOUNT = 0x0008 FILTER_WORKSTATION_TRUST_ACCOUNT = 0x0010 FILTER_SERVER_TRUST_ACCOUNT = 0x0020 MAX_PREFERRED_LENGTH = 0xFFFF NERR_Success = 0 NERR_UserNotFound = 2221 ffi_lib "netapi32" class USER_INFO_3 < FFI::Struct layout :usri3_name, :LPWSTR, :usri3_password, :LPWSTR, :usri3_password_age, :DWORD, :usri3_priv, :DWORD, :usri3_home_dir, :LPWSTR, :usri3_comment, :LPWSTR, :usri3_flags, :DWORD, :usri3_script_path, :LPWSTR, :usri3_auth_flags, :DWORD, :usri3_full_name, :LPWSTR, :usri3_usr_comment, :LPWSTR, :usri3_parms, :LPWSTR, :usri3_workstations, :LPWSTR, :usri3_last_logon, :DWORD, :usri3_last_logoff, :DWORD, :usri3_acct_expires, :DWORD, :usri3_max_storage, :DWORD, :usri3_units_per_week, :DWORD, :usri3_logon_hours, :PBYTE, :usri3_bad_pw_count, :DWORD, :usri3_num_logons, :DWORD, :usri3_logon_server, :LPWSTR, :usri3_country_code, :DWORD, :usri3_code_page, :DWORD, :usri3_user_id, :DWORD, :usri3_primary_group_id, :DWORD, :usri3_profile, :LPWSTR, :usri3_home_dir_drive, :LPWSTR, :usri3_password_expired, :DWORD end # NET_API_STATUS NetUserEnum( # _In_ LPCWSTR servername, # _In_ DWORD level, # _In_ DWORD filter, # _Out_ LPBYTE *bufptr, # _In_ DWORD prefmaxlen, # _Out_ LPDWORD entriesread, # _Out_ LPDWORD totalentries, # _Inout_ LPDWORD resume_handle # ); safe_attach_function :NetUserEnum, [ :LPCWSTR, :DWORD, :DWORD, :LPBYTE, :DWORD, :LPDWORD, :LPDWORD, :LPDWORD ], :DWORD # NET_API_STATUS NetApiBufferFree( # _In_ LPVOID Buffer # ); safe_attach_function :NetApiBufferFree, [ :LPVOID ], :DWORD end end end end chef-12.3.0/lib/chef/win32/api/error.rb0000644000004100000410000012777412520074675017430 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api' class Chef module ReservedNames::Win32 module API module Error extend Chef::ReservedNames::Win32::API ############################################### # Win32 API Constants ############################################### S_OK = 0 NO_ERROR = 0 ERROR_SUCCESS = 0 ERROR_INVALID_FUNCTION = 1 ERROR_FILE_NOT_FOUND = 2 ERROR_PATH_NOT_FOUND = 3 ERROR_TOO_MANY_OPEN_FILES = 4 ERROR_ACCESS_DENIED = 5 ERROR_INVALID_HANDLE = 6 ERROR_ARENA_TRASHED = 7 ERROR_NOT_ENOUGH_MEMORY = 8 ERROR_INVALID_BLOCK = 9 ERROR_BAD_ENVIRONMENT = 10 ERROR_BAD_FORMAT = 11 ERROR_INVALID_ACCESS = 12 ERROR_INVALID_DATA = 13 ERROR_INVALID_DRIVE = 15 ERROR_CURRENT_DIRECTORY = 16 ERROR_NOT_SAME_DEVICE = 17 ERROR_NO_MORE_FILES = 18 ERROR_WRITE_PROTECT = 19 ERROR_BAD_UNIT = 20 ERROR_NOT_READY = 21 ERROR_BAD_COMMAND = 22 ERROR_CRC = 23 ERROR_BAD_LENGTH = 24 ERROR_SEEK = 25 ERROR_NOT_DOS_DISK = 26 ERROR_SECTOR_NOT_FOUND = 27 ERROR_OUT_OF_PAPER = 28 ERROR_WRITE_FAULT = 29 ERROR_READ_FAULT = 30 ERROR_GEN_FAILURE = 31 ERROR_SHARING_VIOLATION = 32 ERROR_LOCK_VIOLATION = 33 ERROR_WRONG_DISK = 34 ERROR_FCB_UNAVAILABLE = 35 # gets returned for some unsuccessful DeviceIoControl calls ERROR_SHARING_BUFFER_EXCEEDED = 36 ERROR_HANDLE_EOF = 38 ERROR_HANDLE_DISK_FULL = 39 ERROR_NOT_SUPPORTED = 50 ERROR_REM_NOT_LIST = 51 ERROR_DUP_NAME = 52 ERROR_BAD_NETPATH = 53 ERROR_NETWORK_BUSY = 54 ERROR_DEV_NOT_EXIST = 55 ERROR_TOO_MANY_CMDS = 56 ERROR_ADAP_HDW_ERR = 57 ERROR_BAD_NET_RESP = 58 ERROR_UNEXP_NET_ERR = 59 ERROR_BAD_REM_ADAP = 60 ERROR_PRINTQ_FULL = 61 ERROR_NO_SPOOL_SPACE = 62 ERROR_PRINT_CANCELLED = 63 ERROR_NETNAME_DELETED = 64 ERROR_NETWORK_ACCESS_DENIED = 65 ERROR_BAD_DEV_TYPE = 66 ERROR_BAD_NET_NAME = 67 ERROR_TOO_MANY_NAMES = 68 ERROR_TOO_MANY_SESS = 69 ERROR_SHARING_PAUSED = 70 ERROR_REQ_NOT_ACCEP = 71 ERROR_REDIR_PAUSED = 72 ERROR_FILE_EXISTS = 80 ERROR_DUP_FCB = 81 ERROR_CANNOT_MAKE = 82 ERROR_FAIL_I24 = 83 ERROR_OUT_OF_STRUCTURES = 84 ERROR_ALREADY_ASSIGNED = 85 ERROR_INVALID_PASSWORD = 86 ERROR_INVALID_PARAMETER = 87 ERROR_NET_WRITE_FAULT = 88 ERROR_NO_PROC_SLOTS = 89 # no process slots available ERROR_NOT_FROZEN = 90 ERR_TSTOVFL = 91 # timer service table overflow ERR_TSTDUP = 92 # timer service table duplicate ERROR_NO_ITEMS = 93 # There were no items to operate upon ERROR_INTERRUPT = 95 # interrupted system call ERROR_TOO_MANY_SEMAPHORES = 100 ERROR_EXCL_SEM_ALREADY_OWNED = 101 ERROR_SEM_IS_SET = 102 ERROR_TOO_MANY_SEM_REQUESTS = 103 ERROR_INVALID_AT_INTERRUPT_TIME = 104 ERROR_SEM_OWNER_DIED = 105 # waitsem found owner died ERROR_SEM_USER_LIMIT = 106 # too many procs have this sem ERROR_DISK_CHANGE = 107 # insert disk b into drive a ERROR_DRIVE_LOCKED = 108 # drive locked by another process ERROR_BROKEN_PIPE = 109 # write on pipe with no reader ERROR_OPEN_FAILED = 110 # open/created failed ERROR_DISK_FULL = 112 # not enough space ERROR_NO_MORE_SEARCH_HANDLES = 113 # can't allocate ERROR_INVALID_TARGET_HANDLE = 114 # handle in DOSDUPHANDLE is invalid ERROR_PROTECTION_VIOLATION = 115 # bad user virtual address ERROR_VIOKBD_REQUEST = 116 ERROR_INVALID_CATEGORY = 117 # category for DEVIOCTL not defined ERROR_INVALID_VERIFY_SWITCH = 118 # invalid value ERROR_BAD_DRIVER_LEVEL = 119 # DosDevIOCTL not level four ERROR_CALL_NOT_IMPLEMENTED = 120 ERROR_SEM_TIMEOUT = 121 # timeout from semaphore function ERROR_INSUFFICIENT_BUFFER = 122 ERROR_INVALID_NAME = 123 # illegal char or malformed file system name ERROR_INVALID_LEVEL = 124 # unimplemented level for info retrieval ERROR_NO_VOLUME_LABEL = 125 # no volume label found ERROR_MOD_NOT_FOUND = 126 # w_getprocaddr, w_getmodhandle ERROR_PROC_NOT_FOUND = 127 # w_getprocaddr ERROR_WAIT_NO_CHILDREN = 128 # CWait finds to children ERROR_CHILD_NOT_COMPLETE = 129 # CWait children not dead yet ERROR_DIRECT_ACCESS_HANDLE = 130 # invalid for direct disk access ERROR_NEGATIVE_SEEK = 131 # tried to seek negative offset ERROR_SEEK_ON_DEVICE = 132 # tried to seek on device or pipe ERROR_IS_JOIN_TARGET = 133 ERROR_IS_JOINED = 134 ERROR_IS_SUBSTED = 135 ERROR_NOT_JOINED = 136 ERROR_NOT_SUBSTED = 137 ERROR_JOIN_TO_JOIN = 138 ERROR_SUBST_TO_SUBST = 139 ERROR_JOIN_TO_SUBST = 140 ERROR_SUBST_TO_JOIN = 141 ERROR_BUSY_DRIVE = 142 ERROR_SAME_DRIVE = 143 ERROR_DIR_NOT_ROOT = 144 ERROR_DIR_NOT_EMPTY = 145 ERROR_IS_SUBST_PATH = 146 ERROR_IS_JOIN_PATH = 147 ERROR_PATH_BUSY = 148 ERROR_IS_SUBST_TARGET = 149 ERROR_SYSTEM_TRACE = 150 # system trace error ERROR_INVALID_EVENT_COUNT = 151 # DosMuxSemWait errors ERROR_TOO_MANY_MUXWAITERS = 152 ERROR_INVALID_LIST_FORMAT = 153 ERROR_LABEL_TOO_LONG = 154 ERROR_TOO_MANY_TCBS = 155 ERROR_SIGNAL_REFUSED = 156 ERROR_DISCARDED = 157 ERROR_NOT_LOCKED = 158 ERROR_BAD_THREADID_ADDR = 159 ERROR_BAD_ARGUMENTS = 160 ERROR_BAD_PATHNAME = 161 ERROR_SIGNAL_PENDING = 162 ERROR_UNCERTAIN_MEDIA = 163 ERROR_MAX_THRDS_REACHED = 164 ERROR_MONITORS_NOT_SUPPORTED = 165 ERROR_LOCK_FAILED = 167 ERROR_BUSY = 170 ERROR_CANCEL_VIOLATION = 173 ERROR_ATOMIC_LOCKS_NOT_SUPPORTED= 174 ERROR_INVALID_SEGMENT_NUMBER = 180 ERROR_INVALID_CALLGATE = 181 ERROR_INVALID_ORDINAL = 182 ERROR_ALREADY_EXISTS = 183 ERROR_NO_CHILD_PROCESS = 184 ERROR_CHILD_ALIVE_NOWAIT = 185 ERROR_INVALID_FLAG_NUMBER = 186 ERROR_SEM_NOT_FOUND = 187 ERROR_INVALID_STARTING_CODESEG = 188 ERROR_INVALID_STACKSEG = 189 ERROR_INVALID_MODULETYPE = 190 ERROR_INVALID_EXE_SIGNATURE = 191 ERROR_EXE_MARKED_INVALID = 192 ERROR_BAD_EXE_FORMAT = 193 ERROR_ITERATED_DATA_EXCEEDS_64k = 194 ERROR_INVALID_MINALLOCSIZE = 195 ERROR_DYNLINK_FROM_INVALID_RING = 196 ERROR_IOPL_NOT_ENABLED = 197 ERROR_INVALID_SEGDPL = 198 ERROR_AUTODATASEG_EXCEEDS_64k = 199 ERROR_RING2SEG_MUST_BE_MOVABLE = 200 ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 201 ERROR_INFLOOP_IN_RELOC_CHAIN = 202 ERROR_ENVVAR_NOT_FOUND = 203 ERROR_NOT_CURRENT_CTRY = 204 ERROR_NO_SIGNAL_SENT = 205 ERROR_FILENAME_EXCED_RANGE = 206 # if filename > 8.3 ERROR_RING2_STACK_IN_USE = 207 # for FAPI ERROR_META_EXPANSION_TOO_LONG = 208 # if "*a" > 8.3 ERROR_INVALID_SIGNAL_NUMBER = 209 ERROR_THREAD_1_INACTIVE = 210 ERROR_INFO_NOT_AVAIL = 211 #@@ PTM 5550 ERROR_LOCKED = 212 ERROR_BAD_DYNALINK = 213 #@@ PTM 5760 ERROR_TOO_MANY_MODULES = 214 ERROR_NESTING_NOT_ALLOWED = 215 ERROR_EXE_MACHINE_TYPE_MISMATCH = 216 ERROR_BAD_PIPE = 230 ERROR_PIPE_BUSY = 231 ERROR_NO_DATA = 232 ERROR_PIPE_NOT_CONNECTED = 233 ERROR_MORE_DATA = 234 ERROR_VC_DISCONNECTED = 240 ERROR_INVALID_EA_NAME = 254 ERROR_EA_LIST_INCONSISTENT = 255 ERROR_NO_MORE_ITEMS = 259 ERROR_CANNOT_COPY = 266 ERROR_DIRECTORY = 267 ERROR_EAS_DIDNT_FIT = 275 ERROR_EA_FILE_CORRUPT = 276 ERROR_EA_TABLE_FULL = 277 ERROR_INVALID_EA_HANDLE = 278 ERROR_EAS_NOT_SUPPORTED = 282 ERROR_NOT_OWNER = 288 ERROR_TOO_MANY_POSTS = 298 ERROR_PARTIAL_COPY = 299 ERROR_OPLOCK_NOT_GRANTED = 300 ERROR_INVALID_OPLOCK_PROTOCOL = 301 ERROR_DISK_TOO_FRAGMENTED = 302 ERROR_MR_MID_NOT_FOUND = 317 ERROR_SCOPE_NOT_FOUND = 318 ERROR_FAIL_NOACTION_REBOOT = 350 ERROR_FAIL_SHUTDOWN = 351 ERROR_FAIL_RESTART = 352 ERROR_MAX_SESSIONS_REACHED = 353 ERROR_INVALID_ADDRESS = 487 ERROR_USER_PROFILE_LOAD = 500 ERROR_ARITHMETIC_OVERFLOW = 534 ERROR_PIPE_CONNECTED = 535 ERROR_PIPE_LISTENING = 536 ERROR_EA_ACCESS_DENIED = 994 ERROR_OPERATION_ABORTED = 995 ERROR_IO_INCOMPLETE = 996 ERROR_IO_PENDING = 997 ERROR_NOACCESS = 998 ERROR_SWAPERROR = 999 ERROR_STACK_OVERFLOW = 1001 ERROR_INVALID_MESSAGE = 1002 ERROR_CAN_NOT_COMPLETE = 1003 ERROR_INVALID_FLAGS = 1004 ERROR_UNRECOGNIZED_VOLUME = 1005 ERROR_FILE_INVALID = 1006 ERROR_FULLSCREEN_MODE = 1007 ERROR_NO_TOKEN = 1008 ERROR_BADDB = 1009 ERROR_BADKEY = 1010 ERROR_CANTOPEN = 1011 ERROR_CANTREAD = 1012 ERROR_CANTWRITE = 1013 ERROR_REGISTRY_RECOVERED = 1014 ERROR_REGISTRY_CORRUPT = 1015 ERROR_REGISTRY_IO_FAILED = 1016 ERROR_NOT_REGISTRY_FILE = 1017 ERROR_KEY_DELETED = 1018 ERROR_NO_LOG_SPACE = 1019 ERROR_KEY_HAS_CHILDREN = 1020 ERROR_CHILD_MUST_BE_VOLATILE = 1021 ERROR_NOTIFY_ENUM_DIR = 1022 ERROR_DEPENDENT_SERVICES_RUNNING = 1051 ERROR_INVALID_SERVICE_CONTROL = 1052 ERROR_SERVICE_REQUEST_TIMEOUT = 1053 ERROR_SERVICE_NO_THREAD = 1054 ERROR_SERVICE_DATABASE_LOCKED = 1055 ERROR_SERVICE_ALREADY_RUNNING = 1056 ERROR_INVALID_SERVICE_ACCOUNT = 1057 ERROR_SERVICE_DISABLED = 1058 ERROR_CIRCULAR_DEPENDENCY = 1059 ERROR_SERVICE_DOES_NOT_EXIST = 1060 ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061 ERROR_SERVICE_NOT_ACTIVE = 1062 ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063 ERROR_EXCEPTION_IN_SERVICE = 1064 ERROR_DATABASE_DOES_NOT_EXIST = 1065 ERROR_SERVICE_SPECIFIC_ERROR = 1066 ERROR_PROCESS_ABORTED = 1067 ERROR_SERVICE_DEPENDENCY_FAIL = 1068 ERROR_SERVICE_LOGON_FAILED = 1069 ERROR_SERVICE_START_HANG = 1070 ERROR_INVALID_SERVICE_LOCK = 1071 ERROR_SERVICE_MARKED_FOR_DELETE = 1072 ERROR_SERVICE_EXISTS = 1073 ERROR_ALREADY_RUNNING_LKG = 1074 ERROR_SERVICE_DEPENDENCY_DELETED = 1075 ERROR_BOOT_ALREADY_ACCEPTED = 1076 ERROR_SERVICE_NEVER_STARTED = 1077 ERROR_DUPLICATE_SERVICE_NAME = 1078 ERROR_DIFFERENT_SERVICE_ACCOUNT = 1079 ERROR_CANNOT_DETECT_DRIVER_FAILURE = 1080 ERROR_CANNOT_DETECT_PROCESS_ABORT = 1081 ERROR_NO_RECOVERY_PROGRAM = 1082 ERROR_SERVICE_NOT_IN_EXE = 1083 ERROR_END_OF_MEDIA = 1100 ERROR_FILEMARK_DETECTED = 1101 ERROR_BEGINNING_OF_MEDIA = 1102 ERROR_SETMARK_DETECTED = 1103 ERROR_NO_DATA_DETECTED = 1104 ERROR_PARTITION_FAILURE = 1105 ERROR_INVALID_BLOCK_LENGTH = 1106 ERROR_DEVICE_NOT_PARTITIONED = 1107 ERROR_UNABLE_TO_LOCK_MEDIA = 1108 ERROR_UNABLE_TO_UNLOAD_MEDIA = 1109 ERROR_MEDIA_CHANGED = 1110 ERROR_BUS_RESET = 1111 ERROR_NO_MEDIA_IN_DRIVE = 1112 ERROR_NO_UNICODE_TRANSLATION = 1113 ERROR_DLL_INIT_FAILED = 1114 ERROR_SHUTDOWN_IN_PROGRESS = 1115 ERROR_NO_SHUTDOWN_IN_PROGRESS = 1116 ERROR_IO_DEVICE = 1117 ERROR_SERIAL_NO_DEVICE = 1118 ERROR_IRQ_BUSY = 1119 ERROR_MORE_WRITES = 1120 ERROR_COUNTER_TIMEOUT = 1121 ERROR_FLOPPY_ID_MARK_NOT_FOUND = 1122 ERROR_FLOPPY_WRONG_CYLINDER = 1123 ERROR_FLOPPY_UNKNOWN_ERROR = 1124 ERROR_FLOPPY_BAD_REGISTERS = 1125 ERROR_DISK_RECALIBRATE_FAILED = 1126 ERROR_DISK_OPERATION_FAILED = 1127 ERROR_DISK_RESET_FAILED = 1128 ERROR_EOM_OVERFLOW = 1129 ERROR_NOT_ENOUGH_SERVER_MEMORY = 1130 ERROR_POSSIBLE_DEADLOCK = 1131 ERROR_MAPPED_ALIGNMENT = 1132 ERROR_SET_POWER_STATE_VETOED = 1140 ERROR_SET_POWER_STATE_FAILED = 1141 ERROR_TOO_MANY_LINKS = 1142 ERROR_OLD_WIN_VERSION = 1150 ERROR_APP_WRONG_OS = 1151 ERROR_SINGLE_INSTANCE_APP = 1152 ERROR_RMODE_APP = 1153 ERROR_INVALID_DLL = 1154 ERROR_NO_ASSOCIATION = 1155 ERROR_DDE_FAIL = 1156 ERROR_DLL_NOT_FOUND = 1157 ERROR_NO_MORE_USER_HANDLES = 1158 ERROR_MESSAGE_SYNC_ONLY = 1159 ERROR_SOURCE_ELEMENT_EMPTY = 1160 ERROR_DESTINATION_ELEMENT_FULL = 1161 ERROR_ILLEGAL_ELEMENT_ADDRESS = 1162 ERROR_MAGAZINE_NOT_PRESENT = 1163 ERROR_DEVICE_REINITIALIZATION_NEEDED = 1164 ERROR_DEVICE_REQUIRES_CLEANING = 1165 ERROR_DEVICE_DOOR_OPEN = 1166 ERROR_DEVICE_NOT_CONNECTED = 1167 ERROR_NOT_FOUND = 1168 ERROR_NO_MATCH = 1169 ERROR_SET_NOT_FOUND = 1170 ERROR_POINT_NOT_FOUND = 1171 ERROR_NO_TRACKING_SERVICE = 1172 ERROR_NO_VOLUME_ID = 1173 ERROR_UNABLE_TO_REMOVE_REPLACED = 1175 ERROR_UNABLE_TO_MOVE_REPLACEMENT = 1176 ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 = 1177 ERROR_JOURNAL_DELETE_IN_PROGRESS = 1178 ERROR_JOURNAL_NOT_ACTIVE = 1179 ERROR_POTENTIAL_FILE_FOUND = 1180 ERROR_JOURNAL_ENTRY_DELETED = 1181 ERROR_BAD_DEVICE = 1200 ERROR_CONNECTION_UNAVAIL = 1201 ERROR_DEVICE_ALREADY_REMEMBERED = 1202 ERROR_NO_NET_OR_BAD_PATH = 1203 ERROR_BAD_PROVIDER = 1204 ERROR_CANNOT_OPEN_PROFILE = 1205 ERROR_BAD_PROFILE = 1206 ERROR_NOT_CONTAINER = 1207 ERROR_EXTENDED_ERROR = 1208 ERROR_INVALID_GROUPNAME = 1209 ERROR_INVALID_COMPUTERNAME = 1210 ERROR_INVALID_EVENTNAME = 1211 ERROR_INVALID_DOMAINNAME = 1212 ERROR_INVALID_SERVICENAME = 1213 ERROR_INVALID_NETNAME = 1214 ERROR_INVALID_SHARENAME = 1215 ERROR_INVALID_PASSWORDNAME = 1216 ERROR_INVALID_MESSAGENAME = 1217 ERROR_INVALID_MESSAGEDEST = 1218 ERROR_SESSION_CREDENTIAL_CONFLICT = 1219 ERROR_REMOTE_SESSION_LIMIT_EXCEEDED = 1220 ERROR_DUP_DOMAINNAME = 1221 ERROR_NO_NETWORK = 1222 ERROR_CANCELLED = 1223 ERROR_USER_MAPPED_FILE = 1224 ERROR_CONNECTION_REFUSED = 1225 ERROR_GRACEFUL_DISCONNECT = 1226 ERROR_ADDRESS_ALREADY_ASSOCIATED = 1227 ERROR_ADDRESS_NOT_ASSOCIATED = 1228 ERROR_CONNECTION_INVALID = 1229 ERROR_CONNECTION_ACTIVE = 1230 ERROR_NETWORK_UNREACHABLE = 1231 ERROR_HOST_UNREACHABLE = 1232 ERROR_PROTOCOL_UNREACHABLE = 1233 ERROR_PORT_UNREACHABLE = 1234 ERROR_REQUEST_ABORTED = 1235 ERROR_CONNECTION_ABORTED = 1236 ERROR_RETRY = 1237 ERROR_CONNECTION_COUNT_LIMIT = 1238 ERROR_LOGIN_TIME_RESTRICTION = 1239 ERROR_LOGIN_WKSTA_RESTRICTION = 1240 ERROR_INCORRECT_ADDRESS = 1241 ERROR_ALREADY_REGISTERED = 1242 ERROR_SERVICE_NOT_FOUND = 1243 ERROR_NOT_AUTHENTICATED = 1244 ERROR_NOT_LOGGED_ON = 1245 ERROR_CONTINUE = 1246 ERROR_ALREADY_INITIALIZED = 1247 ERROR_NO_MORE_DEVICES = 1248 ERROR_NO_SUCH_SITE = 1249 ERROR_DOMAIN_CONTROLLER_EXISTS = 1250 ERROR_ONLY_IF_CONNECTED = 1251 ERROR_OVERRIDE_NOCHANGES = 1252 ERROR_BAD_USER_PROFILE = 1253 ERROR_NOT_SUPPORTED_ON_SBS = 1254 ERROR_SERVER_SHUTDOWN_IN_PROGRESS = 1255 ERROR_HOST_DOWN = 1256 ERROR_ACCESS_DISABLED_BY_POLICY = 1260 ERROR_REG_NAT_CONSUMPTION = 1261 ERROR_PKINIT_FAILURE = 1263 ERROR_SMARTCARD_SUBSYSTEM_FAILURE = 1264 ERROR_DOWNGRADE_DETECTED = 1265 ERROR_MACHINE_LOCKED = 1271 ERROR_CALLBACK_SUPPLIED_INVALID_DATA = 1273 ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED= 1274 ERROR_DRIVER_BLOCKED = 1275 ERROR_INVALID_IMPORT_OF_NON_DLL = 1276 ERROR_NOT_ALL_ASSIGNED = 1300 ERROR_SOME_NOT_MAPPED = 1301 ERROR_NO_QUOTAS_FOR_ACCOUNT = 1302 ERROR_LOCAL_USER_SESSION_KEY = 1303 ERROR_NULL_LM_PASSWORD = 1304 ERROR_UNKNOWN_REVISION = 1305 ERROR_REVISION_MISMATCH = 1306 ERROR_INVALID_OWNER = 1307 ERROR_INVALID_PRIMARY_GROUP = 1308 ERROR_NO_IMPERSONATION_TOKEN = 1309 ERROR_CANT_DISABLE_MANDATORY = 1310 ERROR_NO_LOGON_SERVERS = 1311 ERROR_NO_SUCH_LOGON_SESSION = 1312 ERROR_NO_SUCH_PRIVILEGE = 1313 ERROR_PRIVILEGE_NOT_HELD = 1314 ERROR_INVALID_ACCOUNT_NAME = 1315 ERROR_USER_EXISTS = 1316 ERROR_NO_SUCH_USER = 1317 ERROR_GROUP_EXISTS = 1318 ERROR_NO_SUCH_GROUP = 1319 ERROR_MEMBER_IN_GROUP = 1320 ERROR_MEMBER_NOT_IN_GROUP = 1321 ERROR_LAST_ADMIN = 1322 ERROR_WRONG_PASSWORD = 1323 ERROR_ILL_FORMED_PASSWORD = 1324 ERROR_PASSWORD_RESTRICTION = 1325 ERROR_LOGON_FAILURE = 1326 ERROR_ACCOUNT_RESTRICTION = 1327 ERROR_INVALID_LOGON_HOURS = 1328 ERROR_INVALID_WORKSTATION = 1329 ERROR_PASSWORD_EXPIRED = 1330 ERROR_ACCOUNT_DISABLED = 1331 ERROR_NONE_MAPPED = 1332 ERROR_TOO_MANY_LUIDS_REQUESTED = 1333 ERROR_LUIDS_EXHAUSTED = 1334 ERROR_INVALID_SUB_AUTHORITY = 1335 ERROR_INVALID_ACL = 1336 ERROR_INVALID_SID = 1337 ERROR_INVALID_SECURITY_DESCR = 1338 ERROR_BAD_INHERITANCE_ACL = 1340 ERROR_SERVER_DISABLED = 1341 ERROR_SERVER_NOT_DISABLED = 1342 ERROR_INVALID_ID_AUTHORITY = 1343 ERROR_ALLOTTED_SPACE_EXCEEDED = 1344 ERROR_INVALID_GROUP_ATTRIBUTES = 1345 ERROR_BAD_IMPERSONATION_LEVEL = 1346 ERROR_CANT_OPEN_ANONYMOUS = 1347 ERROR_BAD_VALIDATION_CLASS = 1348 ERROR_BAD_TOKEN_TYPE = 1349 ERROR_NO_SECURITY_ON_OBJECT = 1350 ERROR_CANT_ACCESS_DOMAIN_INFO = 1351 ERROR_INVALID_SERVER_STATE = 1352 ERROR_INVALID_DOMAIN_STATE = 1353 ERROR_INVALID_DOMAIN_ROLE = 1354 ERROR_NO_SUCH_DOMAIN = 1355 ERROR_DOMAIN_EXISTS = 1356 ERROR_DOMAIN_LIMIT_EXCEEDED = 1357 ERROR_INTERNAL_DB_CORRUPTION = 1358 ERROR_INTERNAL_ERROR = 1359 ERROR_GENERIC_NOT_MAPPED = 1360 ERROR_BAD_DESCRIPTOR_FORMAT = 1361 ERROR_NOT_LOGON_PROCESS = 1362 ERROR_LOGON_SESSION_EXISTS = 1363 ERROR_NO_SUCH_PACKAGE = 1364 ERROR_BAD_LOGON_SESSION_STATE = 1365 ERROR_LOGON_SESSION_COLLISION = 1366 ERROR_INVALID_LOGON_TYPE = 1367 ERROR_CANNOT_IMPERSONATE = 1368 ERROR_RXACT_INVALID_STATE = 1369 ERROR_RXACT_COMMIT_FAILURE = 1370 ERROR_SPECIAL_ACCOUNT = 1371 ERROR_SPECIAL_GROUP = 1372 ERROR_SPECIAL_USER = 1373 ERROR_MEMBERS_PRIMARY_GROUP = 1374 ERROR_TOKEN_ALREADY_IN_USE = 1375 ERROR_NO_SUCH_ALIAS = 1376 ERROR_MEMBER_NOT_IN_ALIAS = 1377 ERROR_MEMBER_IN_ALIAS = 1378 ERROR_ALIAS_EXISTS = 1379 ERROR_LOGON_NOT_GRANTED = 1380 ERROR_TOO_MANY_SECRETS = 1381 ERROR_SECRET_TOO_LONG = 1382 ERROR_INTERNAL_DB_ERROR = 1383 ERROR_TOO_MANY_CONTEXT_IDS = 1384 ERROR_LOGON_TYPE_NOT_GRANTED = 1385 ERROR_NT_CROSS_ENCRYPTION_REQUIRED = 1386 ERROR_NO_SUCH_MEMBER = 1387 ERROR_INVALID_MEMBER = 1388 ERROR_TOO_MANY_SIDS = 1389 ERROR_LM_CROSS_ENCRYPTION_REQUIRED = 1390 ERROR_NO_INHERITANCE = 1391 ERROR_FILE_CORRUPT = 1392 ERROR_DISK_CORRUPT = 1393 ERROR_NO_USER_SESSION_KEY = 1394 ERROR_LICENSE_QUOTA_EXCEEDED = 1395 ERROR_WRONG_TARGET_NAME = 1396 ERROR_MUTUAL_AUTH_FAILED = 1397 ERROR_TIME_SKEW = 1398 ERROR_CURRENT_DOMAIN_NOT_ALLOWED = 1399 ERROR_INVALID_WINDOW_HANDLE = 1400 ERROR_INVALID_MENU_HANDLE = 1401 ERROR_INVALID_CURSOR_HANDLE = 1402 ERROR_INVALID_ACCEL_HANDLE = 1403 ERROR_INVALID_HOOK_HANDLE = 1404 ERROR_INVALID_DWP_HANDLE = 1405 ERROR_TLW_WITH_WSCHILD = 1406 ERROR_CANNOT_FIND_WND_CLASS = 1407 ERROR_WINDOW_OF_OTHER_THREAD = 1408 ERROR_HOTKEY_ALREADY_REGISTERED = 1409 ERROR_CLASS_ALREADY_EXISTS = 1410 ERROR_CLASS_DOES_NOT_EXIST = 1411 ERROR_CLASS_HAS_WINDOWS = 1412 ERROR_INVALID_INDEX = 1413 ERROR_INVALID_ICON_HANDLE = 1414 ERROR_PRIVATE_DIALOG_INDEX = 1415 ERROR_LISTBOX_ID_NOT_FOUND = 1416 ERROR_NO_WILDCARD_CHARACTERS = 1417 ERROR_CLIPBOARD_NOT_OPEN = 1418 ERROR_HOTKEY_NOT_REGISTERED = 1419 ERROR_WINDOW_NOT_DIALOG = 1420 ERROR_CONTROL_ID_NOT_FOUND = 1421 ERROR_INVALID_COMBOBOX_MESSAGE = 1422 ERROR_WINDOW_NOT_COMBOBOX = 1423 ERROR_INVALID_EDIT_HEIGHT = 1424 ERROR_DC_NOT_FOUND = 1425 ERROR_INVALID_HOOK_FILTER = 1426 ERROR_INVALID_FILTER_PROC = 1427 ERROR_HOOK_NEEDS_HMOD = 1428 ERROR_GLOBAL_ONLY_HOOK = 1429 ERROR_JOURNAL_HOOK_SET = 1430 ERROR_HOOK_NOT_INSTALLED = 1431 ERROR_INVALID_LB_MESSAGE = 1432 ERROR_SETCOUNT_ON_BAD_LB = 1433 ERROR_LB_WITHOUT_TABSTOPS = 1434 ERROR_DESTROY_OBJECT_OF_OTHER_THREAD = 1435 ERROR_CHILD_WINDOW_MENU = 1436 ERROR_NO_SYSTEM_MENU = 1437 ERROR_INVALID_MSGBOX_STYLE = 1438 ERROR_INVALID_SPI_VALUE = 1439 ERROR_SCREEN_ALREADY_LOCKED = 1440 ERROR_HWNDS_HAVE_DIFF_PARENT = 1441 ERROR_NOT_CHILD_WINDOW = 1442 ERROR_INVALID_GW_COMMAND = 1443 ERROR_INVALID_THREAD_ID = 1444 ERROR_NON_MDICHILD_WINDOW = 1445 ERROR_POPUP_ALREADY_ACTIVE = 1446 ERROR_NO_SCROLLBARS = 1447 ERROR_INVALID_SCROLLBAR_RANGE = 1448 ERROR_INVALID_SHOWWIN_COMMAND = 1449 ERROR_NO_SYSTEM_RESOURCES = 1450 ERROR_NONPAGED_SYSTEM_RESOURCES = 1451 ERROR_PAGED_SYSTEM_RESOURCES = 1452 ERROR_WORKING_SET_QUOTA = 1453 ERROR_PAGEFILE_QUOTA = 1454 ERROR_COMMITMENT_LIMIT = 1455 ERROR_MENU_ITEM_NOT_FOUND = 1456 ERROR_INVALID_KEYBOARD_HANDLE = 1457 ERROR_HOOK_TYPE_NOT_ALLOWED = 1458 ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION = 1459 ERROR_TIMEOUT = 1460 ERROR_INVALID_MONITOR_HANDLE = 1461 ERROR_EVENTLOG_FILE_CORRUPT = 1500 ERROR_EVENTLOG_CANT_START = 1501 ERROR_LOG_FILE_FULL = 1502 ERROR_EVENTLOG_FILE_CHANGED = 1503 ERROR_INVALID_TASK_NAME = 1550 ERROR_INVALID_TASK_INDEX = 1551 ERROR_THREAD_ALREADY_IN_TASK = 1552 ERROR_INSTALL_SERVICE_FAILURE = 1601 ERROR_INSTALL_USEREXIT = 1602 ERROR_INSTALL_FAILURE = 1603 ERROR_INSTALL_SUSPEND = 1604 ERROR_UNKNOWN_PRODUCT = 1605 ERROR_UNKNOWN_FEATURE = 1606 ERROR_UNKNOWN_COMPONENT = 1607 ERROR_UNKNOWN_PROPERTY = 1608 ERROR_INVALID_HANDLE_STATE = 1609 ERROR_BAD_CONFIGURATION = 1610 ERROR_INDEX_ABSENT = 1611 ERROR_INSTALL_SOURCE_ABSENT = 1612 ERROR_INSTALL_PACKAGE_VERSION = 1613 ERROR_PRODUCT_UNINSTALLED = 1614 ERROR_BAD_QUERY_SYNTAX = 1615 ERROR_INVALID_FIELD = 1616 ERROR_DEVICE_REMOVED = 1617 ERROR_INSTALL_ALREADY_RUNNING = 1618 ERROR_INSTALL_PACKAGE_OPEN_FAILED = 1619 ERROR_INSTALL_PACKAGE_INVALID = 1620 ERROR_INSTALL_UI_FAILURE = 1621 ERROR_INSTALL_LOG_FAILURE = 1622 ERROR_INSTALL_LANGUAGE_UNSUPPORTED = 1623 ERROR_INSTALL_TRANSFORM_FAILURE = 1624 ERROR_INSTALL_PACKAGE_REJECTED = 1625 ERROR_FUNCTION_NOT_CALLED = 1626 ERROR_FUNCTION_FAILED = 1627 ERROR_INVALID_TABLE = 1628 ERROR_DATATYPE_MISMATCH = 1629 ERROR_UNSUPPORTED_TYPE = 1630 ERROR_CREATE_FAILED = 1631 ERROR_INSTALL_TEMP_UNWRITABLE = 1632 ERROR_INSTALL_PLATFORM_UNSUPPORTED = 1633 ERROR_INSTALL_NOTUSED = 1634 ERROR_PATCH_PACKAGE_OPEN_FAILED = 1635 ERROR_PATCH_PACKAGE_INVALID = 1636 ERROR_PATCH_PACKAGE_UNSUPPORTED = 1637 ERROR_PRODUCT_VERSION = 1638 ERROR_INVALID_COMMAND_LINE = 1639 ERROR_INSTALL_REMOTE_DISALLOWED = 1640 ERROR_SUCCESS_REBOOT_INITIATED = 1641 ERROR_UNKNOWN_PATCH = 1647 RPC_S_INVALID_STRING_BINDING = 1700 RPC_S_WRONG_KIND_OF_BINDING = 1701 RPC_S_INVALID_BINDING = 1702 RPC_S_PROTSEQ_NOT_SUPPORTED = 1703 RPC_S_INVALID_RPC_PROTSEQ = 1704 RPC_S_INVALID_STRING_UUID = 1705 RPC_S_INVALID_ENDPOINT_FORMAT = 1706 RPC_S_INVALID_NET_ADDR = 1707 RPC_S_NO_ENDPOINT_FOUND = 1708 RPC_S_INVALID_TIMEOUT = 1709 RPC_S_OBJECT_NOT_FOUND = 1710 RPC_S_ALREADY_REGISTERED = 1711 RPC_S_TYPE_ALREADY_REGISTERED = 1712 RPC_S_ALREADY_LISTENING = 1713 RPC_S_NO_PROTSEQS_REGISTERED = 1714 RPC_S_NOT_LISTENING = 1715 RPC_S_UNKNOWN_MGR_TYPE = 1716 RPC_S_UNKNOWN_IF = 1717 RPC_S_NO_BINDINGS = 1718 RPC_S_NO_PROTSEQS = 1719 RPC_S_CANT_CREATE_ENDPOINT = 1720 RPC_S_OUT_OF_RESOURCES = 1721 RPC_S_SERVER_UNAVAILABLE = 1722 RPC_S_SERVER_TOO_BUSY = 1723 RPC_S_INVALID_NETWORK_OPTIONS = 1724 RPC_S_NO_CALL_ACTIVE = 1725 RPC_S_CALL_FAILED = 1726 RPC_S_CALL_FAILED_DNE = 1727 RPC_S_PROTOCOL_ERROR = 1728 RPC_S_UNSUPPORTED_TRANS_SYN = 1730 RPC_S_UNSUPPORTED_TYPE = 1732 RPC_S_INVALID_TAG = 1733 RPC_S_INVALID_BOUND = 1734 RPC_S_NO_ENTRY_NAME = 1735 RPC_S_INVALID_NAME_SYNTAX = 1736 RPC_S_UNSUPPORTED_NAME_SYNTAX = 1737 RPC_S_UUID_NO_ADDRESS = 1739 RPC_S_DUPLICATE_ENDPOINT = 1740 RPC_S_UNKNOWN_AUTHN_TYPE = 1741 RPC_S_MAX_CALLS_TOO_SMALL = 1742 RPC_S_STRING_TOO_LONG = 1743 RPC_S_PROTSEQ_NOT_FOUND = 1744 RPC_S_PROCNUM_OUT_OF_RANGE = 1745 RPC_S_BINDING_HAS_NO_AUTH = 1746 RPC_S_UNKNOWN_AUTHN_SERVICE = 1747 RPC_S_UNKNOWN_AUTHN_LEVEL = 1748 RPC_S_INVALID_AUTH_IDENTITY = 1749 RPC_S_UNKNOWN_AUTHZ_SERVICE = 1750 EPT_S_INVALID_ENTRY = 1751 EPT_S_CANT_PERFORM_OP = 1752 EPT_S_NOT_REGISTERED = 1753 RPC_S_NOTHING_TO_EXPORT = 1754 RPC_S_INCOMPLETE_NAME = 1755 RPC_S_INVALID_VERS_OPTION = 1756 RPC_S_NO_MORE_MEMBERS = 1757 RPC_S_NOT_ALL_OBJS_UNEXPORTED = 1758 RPC_S_INTERFACE_NOT_FOUND = 1759 RPC_S_ENTRY_ALREADY_EXISTS = 1760 RPC_S_ENTRY_NOT_FOUND = 1761 RPC_S_NAME_SERVICE_UNAVAILABLE = 1762 RPC_S_INVALID_NAF_ID = 1763 RPC_S_CANNOT_SUPPORT = 1764 RPC_S_NO_CONTEXT_AVAILABLE = 1765 RPC_S_INTERNAL_ERROR = 1766 RPC_S_ZERO_DIVIDE = 1767 RPC_S_ADDRESS_ERROR = 1768 RPC_S_FP_DIV_ZERO = 1769 RPC_S_FP_UNDERFLOW = 1770 RPC_S_FP_OVERFLOW = 1771 RPC_X_NO_MORE_ENTRIES = 1772 RPC_X_SS_CHAR_TRANS_OPEN_FAIL = 1773 RPC_X_SS_CHAR_TRANS_SHORT_FILE = 1774 RPC_X_SS_IN_NULL_CONTEXT = 1775 RPC_X_SS_CONTEXT_DAMAGED = 1777 RPC_X_SS_HANDLES_MISMATCH = 1778 RPC_X_SS_CANNOT_GET_CALL_HANDLE = 1779 RPC_X_NULL_REF_POINTER = 1780 RPC_X_ENUM_VALUE_OUT_OF_RANGE = 1781 RPC_X_BYTE_COUNT_TOO_SMALL = 1782 RPC_X_BAD_STUB_DATA = 1783 ERROR_INVALID_USER_BUFFER = 1784 ERROR_UNRECOGNIZED_MEDIA = 1785 ERROR_NO_TRUST_LSA_SECRET = 1786 ERROR_NO_TRUST_SAM_ACCOUNT = 1787 ERROR_TRUSTED_DOMAIN_FAILURE = 1788 ERROR_TRUSTED_RELATIONSHIP_FAILURE = 1789 ERROR_TRUST_FAILURE = 1790 RPC_S_CALL_IN_PROGRESS = 1791 ERROR_NETLOGON_NOT_STARTED = 1792 ERROR_ACCOUNT_EXPIRED = 1793 ERROR_REDIRECTOR_HAS_OPEN_HANDLES = 1794 ERROR_PRINTER_DRIVER_ALREADY_INSTALLED= 1795 ERROR_UNKNOWN_PORT = 1796 ERROR_UNKNOWN_PRINTER_DRIVER = 1797 ERROR_UNKNOWN_PRINTPROCESSOR = 1798 ERROR_INVALID_SEPARATOR_FILE = 1799 ERROR_INVALID_PRIORITY = 1800 ERROR_INVALID_PRINTER_NAME = 1801 ERROR_PRINTER_ALREADY_EXISTS = 1802 ERROR_INVALID_PRINTER_COMMAND = 1803 ERROR_INVALID_DATATYPE = 1804 ERROR_INVALID_ENVIRONMENT = 1805 RPC_S_NO_MORE_BINDINGS = 1806 ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 1807 ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 1808 ERROR_NOLOGON_SERVER_TRUST_ACCOUNT = 1809 ERROR_DOMAIN_TRUST_INCONSISTENT = 1810 ERROR_SERVER_HAS_OPEN_HANDLES = 1811 ERROR_RESOURCE_DATA_NOT_FOUND = 1812 ERROR_RESOURCE_TYPE_NOT_FOUND = 1813 ERROR_RESOURCE_NAME_NOT_FOUND = 1814 ERROR_RESOURCE_LANG_NOT_FOUND = 1815 ERROR_NOT_ENOUGH_QUOTA = 1816 RPC_S_NO_INTERFACES = 1817 RPC_S_CALL_CANCELLED = 1818 RPC_S_BINDING_INCOMPLETE = 1819 RPC_S_COMM_FAILURE = 1820 RPC_S_UNSUPPORTED_AUTHN_LEVEL = 1821 RPC_S_NO_PRINC_NAME = 1822 RPC_S_NOT_RPC_ERROR = 1823 RPC_S_UUID_LOCAL_ONLY = 1824 RPC_S_SEC_PKG_ERROR = 1825 RPC_S_NOT_CANCELLED = 1826 RPC_X_INVALID_ES_ACTION = 1827 RPC_X_WRONG_ES_VERSION = 1828 RPC_X_WRONG_STUB_VERSION = 1829 RPC_X_INVALID_PIPE_OBJECT = 1830 RPC_X_WRONG_PIPE_ORDER = 1831 RPC_X_WRONG_PIPE_VERSION = 1832 RPC_S_GROUP_MEMBER_NOT_FOUND = 1898 EPT_S_CANT_CREATE = 1899 RPC_S_INVALID_OBJECT = 1900 ERROR_INVALID_TIME = 1901 ERROR_INVALID_FORM_NAME = 1902 ERROR_INVALID_FORM_SIZE = 1903 ERROR_ALREADY_WAITING = 1904 ERROR_PRINTER_DELETED = 1905 ERROR_INVALID_PRINTER_STATE = 1906 ERROR_PASSWORD_MUST_CHANGE = 1907 ERROR_DOMAIN_CONTROLLER_NOT_FOUND = 1908 ERROR_ACCOUNT_LOCKED_OUT = 1909 OR_INVALID_OXID = 1910 OR_INVALID_OID = 1911 OR_INVALID_SET = 1912 RPC_S_SEND_INCOMPLETE = 1913 RPC_S_INVALID_ASYNC_HANDLE = 1914 RPC_S_INVALID_ASYNC_CALL = 1915 RPC_X_PIPE_CLOSED = 1916 RPC_X_PIPE_DISCIPLINE_ERROR = 1917 RPC_X_PIPE_EMPTY = 1918 ERROR_NO_SITENAME = 1919 ERROR_CANT_ACCESS_FILE = 1920 ERROR_CANT_RESOLVE_FILENAME = 1921 RPC_S_ENTRY_TYPE_MISMATCH = 1922 RPC_S_NOT_ALL_OBJS_EXPORTED = 1923 RPC_S_INTERFACE_NOT_EXPORTED = 1924 RPC_S_PROFILE_NOT_ADDED = 1925 RPC_S_PRF_ELT_NOT_ADDED = 1926 RPC_S_PRF_ELT_NOT_REMOVED = 1927 RPC_S_GRP_ELT_NOT_ADDED = 1928 RPC_S_GRP_ELT_NOT_REMOVED = 1929 ERROR_KM_DRIVER_BLOCKED = 1930 ERROR_CONTEXT_EXPIRED = 1931 ERROR_PER_USER_TRUST_QUOTA_EXCEEDED = 1932 ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED = 1933 ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED= 1934 ERROR_AUTHENTICATION_FIREWALL_FAILED = 1935 ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED= 1936 ERROR_INVALID_PIXEL_FORMAT = 2000 ERROR_BAD_DRIVER = 2001 ERROR_INVALID_WINDOW_STYLE = 2002 ERROR_METAFILE_NOT_SUPPORTED = 2003 ERROR_TRANSFORM_NOT_SUPPORTED = 2004 ERROR_CLIPPING_NOT_SUPPORTED = 2005 ERROR_INVALID_CMM = 2010 ERROR_INVALID_PROFILE = 2011 ERROR_TAG_NOT_FOUND = 2012 ERROR_TAG_NOT_PRESENT = 2013 ERROR_DUPLICATE_TAG = 2014 ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE = 2015 ERROR_PROFILE_NOT_FOUND = 2016 ERROR_INVALID_COLORSPACE = 2017 ERROR_ICM_NOT_ENABLED = 2018 ERROR_DELETING_ICM_XFORM = 2019 ERROR_INVALID_TRANSFORM = 2020 ERROR_COLORSPACE_MISMATCH = 2021 ERROR_INVALID_COLORINDEX = 2022 ERROR_CONNECTED_OTHER_PASSWORD = 2108 ERROR_BAD_USERNAME = 2202 ERROR_NOT_CONNECTED = 2250 ERROR_OPEN_FILES = 2401 ERROR_ACTIVE_CONNECTIONS = 2402 ERROR_DEVICE_IN_USE = 2404 ERROR_UNKNOWN_PRINT_MONITOR = 3000 ERROR_USER_DEFINED_BASE = 0xF000 # Flags for FormatMessage function: FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 FORMAT_MESSAGE_FROM_STRING = 0x00000400 FORMAT_MESSAGE_FROM_HMODULE = 0x00000800 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF # Set/GetErrorMode values: SEM_FAILCRITICALERRORS = 0x0001 SEM_NOALIGNMENTFAULTEXCEPT = 0x0004 SEM_NOGPFAULTERRORBOX = 0x0002 SEM_NOOPENFILEERRORBOX = 0x8000 ############################################### # Win32 API Bindings ############################################### ffi_lib 'kernel32', 'user32' =begin DWORD WINAPI FormatMessage( __in DWORD dwFlags, __in_opt LPCVOID lpSource, __in DWORD dwMessageId, __in DWORD dwLanguageId, __out LPTSTR lpBuffer, __in DWORD nSize, __in_opt va_list *Arguments ); =end safe_attach_function :FormatMessageA, [:DWORD, :LPCVOID, :DWORD, :DWORD, :LPTSTR, :DWORD, :varargs], :DWORD safe_attach_function :FormatMessageW, [:DWORD, :LPCVOID, :DWORD, :DWORD, :LPWSTR, :DWORD, :varargs], :DWORD =begin DWORD WINAPI GetLastError(void); =end safe_attach_function :GetLastError, [], :DWORD =begin void WINAPI SetLastError( __in DWORD dwErrCode ); =end safe_attach_function :SetLastError, [:DWORD], :void safe_attach_function :SetLastErrorEx, [:DWORD, :DWORD], :void =begin UINT WINAPI GetErrorMode(void);s =end safe_attach_function :GetErrorMode, [], :uint =begin UINT WINAPI SetErrorMode( __in UINT uMode ); =end safe_attach_function :SetErrorMode, [:UINT], :UINT end end end end chef-12.3.0/lib/chef/win32/api.rb0000644000004100000410000005416512520074675016270 0ustar www-datawww-data# # Author:: Seth Chisamore () # Author:: John Keiser () # Copyright:: Copyright 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 'ffi' require 'chef/reserved_names' require 'chef/exceptions' class Chef module ReservedNames::Win32 module API # Attempts to use FFI's attach_function method to link a native Win32 # function into the calling module. If this fails a dummy method is # defined which when called, raises a helpful exception to the end-user. def safe_attach_function(win32_func, *args) begin attach_function(win32_func.to_sym, *args) rescue FFI::NotFoundError define_method(win32_func.to_sym) do |*margs| raise Chef::Exceptions::Win32APIFunctionNotImplemented, "This version of Windows does not implement the Win32 function [#{win32_func}]." end end end # put shared stuff (like constants) for all raw Win32 API calls def self.extended(host) host.extend FFI::Library host.extend Macros host.ffi_convention :stdcall # Windows-specific type defs (ms-help://MS.MSDNQTR.v90.en/winprog/winprog/windows_data_types.htm): host.typedef :ushort, :ATOM # Atom ~= Symbol: Atom table stores strings and corresponding identifiers. Application # places a string in an atom table and receives a 16-bit integer, called an atom, that # can be used to access the string. Placed string is called an atom name. # See: http://msdn.microsoft.com/en-us/library/ms648708%28VS.85%29.aspx host.typedef :bool, :BOOL host.typedef :bool, :BOOLEAN host.typedef :uchar, :BYTE # Byte (8 bits). Declared as unsigned char #CALLBACK: K, # Win32.API gem-specific ?? MSDN: #define CALLBACK __stdcall host.typedef :char, :CHAR # 8-bit Windows (ANSI) character. See http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx host.typedef :uint32, :COLORREF # Red, green, blue (RGB) color value (32 bits). See COLORREF for more info. host.typedef :uint32, :DWORD # 32-bit unsigned integer. The range is 0 through 4,294,967,295 decimal. host.typedef :uint64, :DWORDLONG # 64-bit unsigned integer. The range is 0 through 18,446,744,073,709,551,615 decimal. host.typedef :ulong, :DWORD_PTR # Unsigned long type for pointer precision. Use when casting a pointer to a long type # to perform pointer arithmetic. (Also commonly used for general 32-bit parameters that have # been extended to 64 bits in 64-bit Windows.) BaseTsd.h: #host.typedef ULONG_PTR DWORD_PTR; host.typedef :uint32, :DWORD32 host.typedef :uint64, :DWORD64 host.typedef :int, :HALF_PTR # Half the size of a pointer. Use within a structure that contains a pointer and two small fields. # BaseTsd.h: #ifdef (_WIN64) host.typedef int HALF_PTR; #else host.typedef short HALF_PTR; host.typedef :ulong, :HACCEL # (L) Handle to an accelerator table. WinDef.h: #host.typedef HANDLE HACCEL; # See http://msdn.microsoft.com/en-us/library/ms645526%28VS.85%29.aspx host.typedef :ulong, :HANDLE # (L) Handle to an object. WinNT.h: #host.typedef PVOID HANDLE; # todo: Platform-dependent! Need to change to :uint64 for Win64 host.typedef :ulong, :HBITMAP # (L) Handle to a bitmap: http://msdn.microsoft.com/en-us/library/dd183377%28VS.85%29.aspx host.typedef :ulong, :HBRUSH # (L) Handle to a brush. http://msdn.microsoft.com/en-us/library/dd183394%28VS.85%29.aspx host.typedef :ulong, :HCOLORSPACE # (L) Handle to a color space. http://msdn.microsoft.com/en-us/library/ms536546%28VS.85%29.aspx host.typedef :ulong, :HCURSOR # (L) Handle to a cursor. http://msdn.microsoft.com/en-us/library/ms646970%28VS.85%29.aspx host.typedef :ulong, :HCONV # (L) Handle to a dynamic data exchange (DDE) conversation. host.typedef :ulong, :HCONVLIST # (L) Handle to a DDE conversation list. HANDLE - L ? host.typedef :ulong, :HDDEDATA # (L) Handle to DDE data (structure?) host.typedef :ulong, :HDC # (L) Handle to a device context (DC). http://msdn.microsoft.com/en-us/library/dd183560%28VS.85%29.aspx host.typedef :ulong, :HDESK # (L) Handle to a desktop. http://msdn.microsoft.com/en-us/library/ms682573%28VS.85%29.aspx host.typedef :ulong, :HDROP # (L) Handle to an internal drop structure. host.typedef :ulong, :HDWP # (L) Handle to a deferred window position structure. host.typedef :ulong, :HENHMETAFILE #(L) Handle to an enhanced metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx host.typedef :uint, :HFILE # (I) Special file handle to a file opened by OpenFile, not CreateFile. # WinDef.h: #host.typedef int HFILE; host.typedef :ulong, :HFONT # (L) Handle to a font. http://msdn.microsoft.com/en-us/library/dd162470%28VS.85%29.aspx host.typedef :ulong, :HGDIOBJ # (L) Handle to a GDI object. host.typedef :ulong, :HGLOBAL # (L) Handle to a global memory block. host.typedef :ulong, :HHOOK # (L) Handle to a hook. http://msdn.microsoft.com/en-us/library/ms632589%28VS.85%29.aspx host.typedef :ulong, :HICON # (L) Handle to an icon. http://msdn.microsoft.com/en-us/library/ms646973%28VS.85%29.aspx host.typedef :ulong, :HINSTANCE # (L) Handle to an instance. This is the base address of the module in memory. # HMODULE and HINSTANCE are the same today, but were different in 16-bit Windows. host.typedef :ulong, :HKEY # (L) Handle to a registry key. host.typedef :ulong, :HKL # (L) Input locale identifier. host.typedef :ulong, :HLOCAL # (L) Handle to a local memory block. host.typedef :ulong, :HMENU # (L) Handle to a menu. http://msdn.microsoft.com/en-us/library/ms646977%28VS.85%29.aspx host.typedef :ulong, :HMETAFILE # (L) Handle to a metafile. http://msdn.microsoft.com/en-us/library/dd145051%28VS.85%29.aspx host.typedef :ulong, :HMODULE # (L) Handle to an instance. Same as HINSTANCE today, but was different in 16-bit Windows. host.typedef :ulong, :HMONITOR # (L) Рandle to a display monitor. WinDef.h: if(WINVER >= 0x0500) host.typedef HANDLE HMONITOR; host.typedef :ulong, :HPALETTE # (L) Handle to a palette. host.typedef :ulong, :HPEN # (L) Handle to a pen. http://msdn.microsoft.com/en-us/library/dd162786%28VS.85%29.aspx host.typedef :long, :HRESULT # Return code used by COM interfaces. For more info, Structure of the COM Error Codes. # To test an HRESULT value, use the FAILED and SUCCEEDED macros. host.typedef :ulong, :HRGN # (L) Handle to a region. http://msdn.microsoft.com/en-us/library/dd162913%28VS.85%29.aspx host.typedef :ulong, :HRSRC # (L) Handle to a resource. host.typedef :ulong, :HSZ # (L) Handle to a DDE string. host.typedef :ulong, :HWINSTA # (L) Handle to a window station. http://msdn.microsoft.com/en-us/library/ms687096%28VS.85%29.aspx host.typedef :ulong, :HWND # (L) Handle to a window. http://msdn.microsoft.com/en-us/library/ms632595%28VS.85%29.aspx host.typedef :int, :INT # 32-bit signed integer. The range is -2147483648 through 2147483647 decimal. host.typedef :int, :INT_PTR # Signed integer type for pointer precision. Use when casting a pointer to an integer # to perform pointer arithmetic. BaseTsd.h: #if defined(_WIN64) host.typedef __int64 INT_PTR; #else host.typedef int INT_PTR; host.typedef :int32, :INT32 # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal. host.typedef :int64, :INT64 # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807 host.typedef :ushort, :LANGID # Language identifier. For more information, see Locales. WinNT.h: #host.typedef WORD LANGID; # See http://msdn.microsoft.com/en-us/library/dd318716%28VS.85%29.aspx host.typedef :uint32, :LCID # Locale identifier. For more information, see Locales. host.typedef :uint32, :LCTYPE # Locale information type. For a list, see Locale Information Constants. host.typedef :uint32, :LGRPID # Language group identifier. For a list, see EnumLanguageGroupLocales. host.typedef :long, :LONG # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal. host.typedef :int32, :LONG32 # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal. host.typedef :int64, :LONG64 # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807 host.typedef :int64, :LONGLONG # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807 host.typedef :long, :LONG_PTR # Signed long type for pointer precision. Use when casting a pointer to a long to # perform pointer arithmetic. BaseTsd.h: #if defined(_WIN64) host.typedef __int64 LONG_PTR; #else host.typedef long LONG_PTR; host.typedef :long, :LPARAM # Message parameter. WinDef.h as follows: #host.typedef LONG_PTR LPARAM; host.typedef :pointer, :LPBOOL # Pointer to a BOOL. WinDef.h as follows: #host.typedef BOOL far *LPBOOL; host.typedef :pointer, :LPBYTE # Pointer to a BYTE. WinDef.h as follows: #host.typedef BYTE far *LPBYTE; host.typedef :pointer, :LPCOLORREF # Pointer to a COLORREF value. WinDef.h as follows: #host.typedef DWORD *LPCOLORREF; host.typedef :pointer, :LPCSTR # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters. # See Character Sets Used By Fonts. http://msdn.microsoft.com/en-us/library/dd183415%28VS.85%29.aspx host.typedef :pointer, :LPCTSTR # An LPCWSTR if UNICODE is defined, an LPCSTR otherwise. host.typedef :pointer, :LPCVOID # Pointer to a constant of any type. WinDef.h as follows: host.typedef CONST void *LPCVOID; host.typedef :pointer, :LPCWSTR # Pointer to a constant null-terminated string of 16-bit Unicode characters. host.typedef :pointer, :LPDWORD # Pointer to a DWORD. WinDef.h as follows: host.typedef DWORD *LPDWORD; host.typedef :pointer, :LPHANDLE # Pointer to a HANDLE. WinDef.h as follows: host.typedef HANDLE *LPHANDLE; host.typedef :pointer, :LPINT # Pointer to an INT. host.typedef :pointer, :LPLONG # Pointer to an LONG. host.typedef :pointer, :LPSECURITY_ATTRIBUTES # Pointer to SECURITY_ATTRIBUTES struct host.typedef :pointer, :LPSTR # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. host.typedef :pointer, :LPTSTR # An LPWSTR if UNICODE is defined, an LPSTR otherwise. host.typedef :pointer, :LPVOID # Pointer to any type. host.typedef :pointer, :LPWORD # Pointer to a WORD. host.typedef :pointer, :LPWSTR # Pointer to a null-terminated string of 16-bit Unicode characters. host.typedef :long, :LRESULT # Signed result of message processing. WinDef.h: host.typedef LONG_PTR LRESULT; host.typedef :pointer, :LPWIN32_FIND_DATA # Pointer to WIN32_FIND_DATA struct host.typedef :pointer, :LPBY_HANDLE_FILE_INFORMATION # Point to a BY_HANDLE_FILE_INFORMATION struct host.typedef :pointer, :PBOOL # Pointer to a BOOL. host.typedef :pointer, :PBOOLEAN # Pointer to a BOOL. host.typedef :pointer, :PBYTE # Pointer to a BYTE. host.typedef :pointer, :PCHAR # Pointer to a CHAR. host.typedef :pointer, :PCSTR # Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters. host.typedef :pointer, :PCTSTR # A PCWSTR if UNICODE is defined, a PCSTR otherwise. host.typedef :pointer, :PCWSTR # Pointer to a constant null-terminated string of 16-bit Unicode characters. host.typedef :pointer, :PDWORD # Pointer to a DWORD. host.typedef :pointer, :PDWORDLONG # Pointer to a DWORDLONG. host.typedef :pointer, :PDWORD_PTR # Pointer to a DWORD_PTR. host.typedef :pointer, :PDWORD32 # Pointer to a DWORD32. host.typedef :pointer, :PDWORD64 # Pointer to a DWORD64. host.typedef :pointer, :PFLOAT # Pointer to a FLOAT. host.typedef :pointer, :PGENERICMAPPING #Pointer to GENERIC_MAPPING host.typedef :pointer, :PHALF_PTR # Pointer to a HALF_PTR. host.typedef :pointer, :PHANDLE # Pointer to a HANDLE. host.typedef :pointer, :PHKEY # Pointer to an HKEY. host.typedef :pointer, :PINT # Pointer to an INT. host.typedef :pointer, :PINT_PTR # Pointer to an INT_PTR. host.typedef :pointer, :PINT32 # Pointer to an INT32. host.typedef :pointer, :PINT64 # Pointer to an INT64. host.typedef :pointer, :PLCID # Pointer to an LCID. host.typedef :pointer, :PLONG # Pointer to a LONG. host.typedef :pointer, :PLONGLONG # Pointer to a LONGLONG. host.typedef :pointer, :PLONG_PTR # Pointer to a LONG_PTR. host.typedef :pointer, :PLONG32 # Pointer to a LONG32. host.typedef :pointer, :PLONG64 # Pointer to a LONG64. host.typedef :pointer, :PLUID # Pointer to a LUID. host.typedef :pointer, :POINTER_32 # 32-bit pointer. On a 32-bit system, this is a native pointer. On a 64-bit system, this is a truncated 64-bit pointer. host.typedef :pointer, :POINTER_64 # 64-bit pointer. On a 64-bit system, this is a native pointer. On a 32-bit system, this is a sign-extended 32-bit pointer. host.typedef :pointer, :POINTER_SIGNED # A signed pointer. host.typedef :pointer, :POINTER_UNSIGNED # An unsigned pointer. host.typedef :pointer, :PSHORT # Pointer to a SHORT. host.typedef :pointer, :PSIZE_T # Pointer to a SIZE_T. host.typedef :pointer, :PSSIZE_T # Pointer to a SSIZE_T. host.typedef :pointer, :PSTR # Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts. host.typedef :pointer, :PTBYTE # Pointer to a TBYTE. host.typedef :pointer, :PTCHAR # Pointer to a TCHAR. host.typedef :pointer, :PCRYPTPROTECT_PROMPTSTRUCT # Pointer to a CRYPTOPROTECT_PROMPTSTRUCT. host.typedef :pointer, :PDATA_BLOB # Pointer to a DATA_BLOB. host.typedef :pointer, :PTSTR # A PWSTR if UNICODE is defined, a PSTR otherwise. host.typedef :pointer, :PUCHAR # Pointer to a UCHAR. host.typedef :pointer, :PUHALF_PTR # Pointer to a UHALF_PTR. host.typedef :pointer, :PUINT # Pointer to a UINT. host.typedef :pointer, :PUINT_PTR # Pointer to a UINT_PTR. host.typedef :pointer, :PUINT32 # Pointer to a UINT32. host.typedef :pointer, :PUINT64 # Pointer to a UINT64. host.typedef :pointer, :PULONG # Pointer to a ULONG. host.typedef :pointer, :PULONGLONG # Pointer to a ULONGLONG. host.typedef :pointer, :PULONG_PTR # Pointer to a ULONG_PTR. host.typedef :pointer, :PULONG32 # Pointer to a ULONG32. host.typedef :pointer, :PULONG64 # Pointer to a ULONG64. host.typedef :pointer, :PUSHORT # Pointer to a USHORT. host.typedef :pointer, :PVOID # Pointer to any type. host.typedef :pointer, :PWCHAR # Pointer to a WCHAR. host.typedef :pointer, :PWORD # Pointer to a WORD. host.typedef :pointer, :PWSTR # Pointer to a null- terminated string of 16-bit Unicode characters. # For more information, see Character Sets Used By Fonts. host.typedef :ulong, :SC_HANDLE # (L) Handle to a service control manager database. # See SCM Handles http://msdn.microsoft.com/en-us/library/ms685104%28VS.85%29.aspx host.typedef :pointer, :SC_LOCK # Lock to a service control manager database. For more information, see SCM Handles. host.typedef :ulong, :SERVICE_STATUS_HANDLE # (L) Handle to a service status value. See SCM Handles. host.typedef :short, :SHORT # A 16-bit integer. The range is –32768 through 32767 decimal. host.typedef :ulong, :SIZE_T # The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer. host.typedef :long, :SSIZE_T # Signed SIZE_T. host.typedef :char, :TBYTE # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR: # http://msdn.microsoft.com/en-us/library/c426s321%28VS.80%29.aspx host.typedef :char, :TCHAR # A WCHAR if UNICODE is defined, a CHAR otherwise.TCHAR: host.typedef :uchar, :UCHAR # Unsigned CHAR (8 bit) host.typedef :uint, :UHALF_PTR # Unsigned HALF_PTR. Use within a structure that contains a pointer and two small fields. host.typedef :uint, :UINT # Unsigned INT. The range is 0 through 4294967295 decimal. host.typedef :uint, :UINT_PTR # Unsigned INT_PTR. host.typedef :uint32, :UINT32 # Unsigned INT32. The range is 0 through 4294967295 decimal. host.typedef :uint64, :UINT64 # Unsigned INT64. The range is 0 through 18446744073709551615 decimal. host.typedef :ulong, :ULONG # Unsigned LONG. The range is 0 through 4294967295 decimal. host.typedef :ulong_long, :ULONGLONG # 64-bit unsigned integer. The range is 0 through 18446744073709551615 decimal. host.typedef :ulong, :ULONG_PTR # Unsigned LONG_PTR. host.typedef :uint32, :ULONG32 # Unsigned INT32. The range is 0 through 4294967295 decimal. host.typedef :uint64, :ULONG64 # Unsigned LONG64. The range is 0 through 18446744073709551615 decimal. host.typedef :pointer, :UNICODE_STRING # Pointer to some string structure?? host.typedef :ushort, :USHORT # Unsigned SHORT. The range is 0 through 65535 decimal. host.typedef :ulong_long, :USN # Update sequence number (USN). host.typedef :ushort, :WCHAR # 16-bit Unicode character. For more information, see Character Sets Used By Fonts. # In WinNT.h: host.typedef wchar_t WCHAR; #WINAPI: K, # Calling convention for system functions. WinDef.h: define WINAPI __stdcall host.typedef :ushort, :WORD # 16-bit unsigned integer. The range is 0 through 65535 decimal. host.typedef :uint, :WPARAM # Message parameter. WinDef.h as follows: host.typedef UINT_PTR WPARAM; end module Macros ############################################### # winbase.h ############################################### def LocalDiscard(pointer) LocalReAlloc(pointer, 0, LMEM_MOVEABLE) end ############################################### # windef.h ############################################### # Creates a WORD value by concatenating the specified values. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632663(v=VS.85).aspx def MAKEWORD(low, high) ((low & 0xff) | (high & 0xff)) << 8 end # Creates a LONG value by concatenating the specified values. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632660(v=vs.85).aspx def MAKELONG(low, high) ((low & 0xffff) | (high & 0xffff)) << 16 end # Retrieves the low-order word from the specified value. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632659(v=VS.85).aspx def LOWORD(l) l & 0xffff end # Retrieves the high-order word from the specified 32-bit value. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632657(v=VS.85).aspx def HIWORD(l) l >> 16 end # Retrieves the low-order byte from the specified value. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632658(v=VS.85).aspx def LOBYTE(w) w & 0xff end # Retrieves the high-order byte from the given 16-bit value. # # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632656(v=VS.85).aspx def HIBYTE(w) w >> 8 end ############################################### # winerror.h ############################################### def IS_ERROR(status) status >> 31 == 1 end def MAKE_HRESULT(sev, fac, code) sev << 31 | fac << 16 | code end def MAKE_SCODE(sev, fac, code) sev << 31 | fac << 16 | code end def HRESULT_CODE(hr) hr & 0xFFFF end def HRESULT_FACILITY(hr) (hr >> 16) & 0x1fff end def HRESULT_FROM_NT(x) x | 0x10000000 # FACILITY_NT_BIT end def HRESULT_FROM_WIN32(x) if x <= 0 x else (x & 0x0000FFFF) | (7 << 16) | 0x80000000 end end def HRESULT_SEVERITY(hr) (hr >> 31) & 0x1 end def FAILED(status) status < 0 end def SUCCEEDED(status) status >= 0 end end # Represents a 64-bit unsigned integer value. # # http://msdn.microsoft.com/en-us/library/windows/desktop/aa383742(v=vs.85).aspx def make_uint64(low, high) low + (high * (2**32)) end # http://blogs.msdn.com/b/oldnewthing/archive/2009/03/06/9461176.aspx # January 1, 1601 WIN32_EPOC_MINUS_POSIX_EPOC = 116444736000000000 # Convert 64-bit FILETIME integer into Time object. # # FILETIME structure contains a 64-bit value representing the number # of 100-nanosecond intervals since January 1, 1601 (UTC). # # http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx # def wtime_to_time(wtime) Time.at((wtime - WIN32_EPOC_MINUS_POSIX_EPOC) / 10000000) end end end end chef-12.3.0/lib/chef/win32/file.rb0000644000004100000410000001620312520074675016425 0ustar www-datawww-data# # Author:: Seth Chisamore () # Author:: Mark Mzyk () # Copyright:: Copyright 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 'chef/win32/api/file' require 'chef/win32/api/security' require 'chef/win32/error' class Chef module ReservedNames::Win32 class File include Chef::ReservedNames::Win32::API::File extend Chef::ReservedNames::Win32::API::File # Creates a symbolic link called +new_name+ for the file or directory # +old_name+. # # This method requires Windows Vista or later to work. Otherwise, it # returns nil as per MRI. # def self.link(old_name, new_name) raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) # TODO do a check for CreateHardLinkW and # raise NotImplemented exception on older Windows old_name = encode_path(old_name) new_name = encode_path(new_name) unless CreateHardLinkW(new_name, old_name, nil) Chef::ReservedNames::Win32::Error.raise! end end # Creates a symbolic link called +new_name+ for the file or directory # +old_name+. # # This method requires Windows Vista or later to work. Otherwise, it # returns nil as per MRI. # def self.symlink(old_name, new_name) # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) # TODO do a check for CreateSymbolicLinkW and # raise NotImplemented exception on older Windows flags = ::File.directory?(old_name) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0 old_name = encode_path(old_name) new_name = encode_path(new_name) unless CreateSymbolicLinkW(new_name, old_name, flags) Chef::ReservedNames::Win32::Error.raise! end end # Return true if the named file is a symbolic link, false otherwise. # # This method requires Windows Vista or later to work. Otherwise, it # always returns false as per MRI. # def self.symlink?(file_name) is_symlink = false path = encode_path(file_name) if ::File.exists?(file_name) if ((GetFileAttributesW(path) & FILE_ATTRIBUTE_REPARSE_POINT) > 0) file_search_handle(file_name) do |handle, find_data| if find_data[:dw_reserved_0] == IO_REPARSE_TAG_SYMLINK is_symlink = true end end end end is_symlink end # Returns the path of the of the symbolic link referred to by +file+. # # Requires Windows Vista or later. On older versions of Windows it # will raise a NotImplementedError, as per MRI. # def self.readlink(link_name) raise Errno::ENOENT, link_name unless ::File.exists?(link_name) symlink_file_handle(link_name) do |handle| # Go to DeviceIoControl to get the symlink information # http://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx reparse_buffer = FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) parsed_size = FFI::Buffer.new(:long).write_long(0) if DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nil, 0, reparse_buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, parsed_size, nil) == 0 Chef::ReservedNames::Win32::Error.raise! end # Ensure it's a symbolic link reparse_buffer = REPARSE_DATA_BUFFER.new(reparse_buffer) if reparse_buffer[:ReparseTag] != IO_REPARSE_TAG_SYMLINK raise Errno::EACCES, "#{link_name} is not a symlink" end # Return the link destination (strip off \??\ at the beginning, which is a local filesystem thing) link_dest = reparse_buffer.reparse_buffer.substitute_name if link_dest =~ /^\\\?\?\\/ link_dest = link_dest[4..-1] end link_dest end end # Gets the short form of a path (Administrator -> ADMINI~1) def self.get_short_path_name(path) path = path.to_wstring size = GetShortPathNameW(path, nil, 0) if size == 0 Chef::ReservedNames::Win32::Error.raise! end result = FFI::MemoryPointer.new :char, (size+1)*2 if GetShortPathNameW(path, result, size+1) == 0 Chef::ReservedNames::Win32::Error.raise! end result.read_wstring(size) end # Gets the long form of a path (ADMINI~1 -> Administrator) def self.get_long_path_name(path) path = path.to_wstring size = GetLongPathNameW(path, nil, 0) if size == 0 Chef::ReservedNames::Win32::Error.raise! end result = FFI::MemoryPointer.new :char, (size+1)*2 if GetLongPathNameW(path, result, size+1) == 0 Chef::ReservedNames::Win32::Error.raise! end result.read_wstring(size) end def self.info(file_name) Info.new(file_name) end def self.verify_links_supported! begin CreateSymbolicLinkW(nil) rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e raise e rescue Exception # things are ok. end end def self.file_access_check(path, desired_access) security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security(path) token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE | Chef::ReservedNames::Win32::Security::TOKEN_QUERY | Chef::ReservedNames::Win32::Security::TOKEN_DUPLICATE | Chef::ReservedNames::Win32::Security::STANDARD_RIGHTS_READ token = Chef::ReservedNames::Win32::Security.open_process_token( Chef::ReservedNames::Win32::Process.get_current_process, token_rights) duplicate_token = token.duplicate_token(:SecurityImpersonation) mapping = Chef::ReservedNames::Win32::Security::GENERIC_MAPPING.new mapping[:GenericRead] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ mapping[:GenericWrite] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_WRITE mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token, desired_access, mapping) end # ::File compat class << self alias :stat :info end end end end require 'chef/win32/file/info' chef-12.3.0/lib/chef/win32/version.rb0000644000004100000410000001602312520074675017173 0ustar www-datawww-data# # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/api' require 'chef/win32/api/system' require 'wmi-lite/wmi' class Chef module ReservedNames::Win32 class Version class << self include Chef::ReservedNames::Win32::API::System end include Chef::ReservedNames::Win32::API::Macros include Chef::ReservedNames::Win32::API::System # Ruby implementation of # http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx # http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx def self.get_system_metrics(n_index) GetSystemMetrics(n_index) end private_class_method :get_system_metrics def self.method_name_from_marketing_name(marketing_name) "#{marketing_name.gsub(/\s/, '_').gsub(/\./, '_').downcase}?" # "#{marketing_name.gsub(/\s/, '_').gsub(//, '_').downcase}?" end private_class_method :method_name_from_marketing_name WIN_VERSIONS = { "Windows 10" => {:major => 6, :minor => 4, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }}, "Windows Server 10" => {:major => 6, :minor => 4, :callable => lambda {|product_type, suite_mask| product_type != VER_NT_WORKSTATION }}, "Windows 8.1" => {:major => 6, :minor => 3, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }}, "Windows Server 2012 R2" => {:major => 6, :minor => 3, :callable => lambda {|product_type, suite_mask| product_type != VER_NT_WORKSTATION }}, "Windows 8" => {:major => 6, :minor => 2, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }}, "Windows Server 2012" => {:major => 6, :minor => 2, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }}, "Windows 7" => {:major => 6, :minor => 1, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }}, "Windows Server 2008 R2" => {:major => 6, :minor => 1, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }}, "Windows Server 2008" => {:major => 6, :minor => 0, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }}, "Windows Vista" => {:major => 6, :minor => 0, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }}, "Windows Server 2003 R2" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| get_system_metrics(SM_SERVERR2) != 0 }}, "Windows Home Server" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| (suite_mask & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER }}, "Windows Server 2003" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| get_system_metrics(SM_SERVERR2) == 0 }}, "Windows XP" => {:major => 5, :minor => 1}, "Windows 2000" => {:major => 5, :minor => 0} } def initialize @major_version, @minor_version, @build_number = get_version ver_info = get_version_ex @product_type = ver_info[:w_product_type] @suite_mask = ver_info[:w_suite_mask] @sp_major_version = ver_info[:w_service_pack_major] @sp_minor_version = ver_info[:w_service_pack_minor] # Obtain sku information for the purpose of identifying # datacenter, cluster, and core skus, the latter 2 only # exist in releases after Windows Server 2003 if ! Chef::Platform::windows_server_2003? @sku = get_product_info(@major_version, @minor_version, @sp_major_version, @sp_minor_version) else # The get_product_info API is not supported on Win2k3, # use an alternative to identify datacenter skus @sku = get_datacenter_product_info_windows_server_2003(ver_info) end end marketing_names = Array.new # General Windows checks WIN_VERSIONS.each do |k,v| method_name = method_name_from_marketing_name(k) define_method(method_name) do (@major_version == v[:major]) && (@minor_version == v[:minor]) && (v[:callable] ? v[:callable].call(@product_type, @suite_mask) : true) end marketing_names << [k, method_name] end define_method(:marketing_name) do marketing_names.each do |mn| break mn[0] if self.send(mn[1]) end end # Server Type checks %w{ cluster core datacenter }.each do |m| define_method("#{m}?") do self.class.constants.any? do |c| (self.class.const_get(c) == @sku) && (c.to_s =~ /#{m}/i ) end end end private def get_version # Use WMI here because API's like GetVersion return faked # version numbers on Windows Server 2012 R2 and Windows 8.1 -- # WMI always returns the truth. See article at # http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx # CHEF-4888: Work around ruby #2618, expected to be fixed in Ruby 2.1.0 # https://github.com/ruby/ruby/commit/588504b20f5cc880ad51827b93e571e32446e5db # https://github.com/ruby/ruby/commit/27ed294c7134c0de582007af3c915a635a6506cd wmi = WmiLite::Wmi.new os_info = wmi.first_of('Win32_OperatingSystem') os_version = os_info['version'] # The operating system version is a string in the following form # that can be split into components based on the '.' delimiter: # MajorVersionNumber.MinorVersionNumber.BuildNumber os_version.split('.').collect { | version_string | version_string.to_i } end def get_version_ex lp_version_info = OSVERSIONINFOEX.new lp_version_info[:dw_os_version_info_size] = OSVERSIONINFOEX.size unless GetVersionExW(lp_version_info) Chef::ReservedNames::Win32::Error.raise! end lp_version_info end def get_product_info(major, minor, sp_major, sp_minor) out = FFI::MemoryPointer.new(:uint32) GetProductInfo(major, minor, sp_major, sp_minor, out) out.get_uint(0) end def get_datacenter_product_info_windows_server_2003(ver_info) # The intent is not to get the actual sku, just identify # Windows Server 2003 datacenter sku = (ver_info[:w_suite_mask] & VER_SUITE_DATACENTER) ? PRODUCT_DATACENTER_SERVER : 0 end end end end chef-12.3.0/lib/chef/win32/unicode.rb0000644000004100000410000000274312520074675017140 0ustar www-datawww-data# # Author:: John Keiser () # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/api/unicode' class Chef module ReservedNames::Win32 class Unicode include Chef::ReservedNames::Win32::API::Unicode extend Chef::ReservedNames::Win32::API::Unicode end end end module FFI class Pointer def read_wstring(num_wchars = nil) if num_wchars.nil? # Find the length of the string length = 0 last_char = nil while last_char != "\000\000" do length += 1 last_char = self.get_bytes(0,length * 2)[-2..-1] end num_wchars = length end Chef::ReservedNames::Win32::Unicode.wide_to_utf8(self.get_bytes(0, num_wchars*2)) end end end class String def to_wstring Chef::ReservedNames::Win32::Unicode.utf8_to_wide(self) end end chef-12.3.0/lib/chef/win32/registry.rb0000644000004100000410000003300012520074675017350 0ustar www-datawww-data# # Author:: Prajakta Purohit () # Author:: Lamont Granquist () # # Copyright:: 2012, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/reserved_names' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'win32/registry' require 'win32/api' end class Chef class Win32 class Registry attr_accessor :run_context attr_accessor :architecture def initialize(run_context=nil, user_architecture=:machine) @run_context = run_context self.architecture = user_architecture end def architecture=(user_architecture) @architecture = user_architecture.to_sym assert_architecture! end def get_values(key_path) hive, key = get_hive_and_key(key_path) key_exists!(key_path) values = hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| reg.map { |name, type, data| {:name=>name, :type=>get_name_from_type(type), :data=>data} } end end def set_value(key_path, value) Chef::Log.debug("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{value[:data]}") key_exists!(key_path) hive, key = get_hive_and_key(key_path) if value_exists?(key_path, value) if data_exists?(key_path, value) Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} already had those values, not updated") return false else hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg| reg.write(value[:name], get_type_from_name(value[:type]), value[:data]) end Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} updated") end else hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg| reg.write(value[:name], get_type_from_name(value[:type]), value[:data]) end Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} created") end true end def delete_value(key_path, value) Chef::Log.debug("Deleting value #{value[:name]} from registry key #{key_path}") if value_exists?(key_path, value) begin hive, key = get_hive_and_key(key_path) rescue Chef::Exceptions::Win32RegKeyMissing return true end hive.open(key, ::Win32::Registry::KEY_SET_VALUE | registry_system_architecture) do |reg| reg.delete_value(value[:name]) Chef::Log.debug("Deleted value #{value[:name]} from registry key #{key_path}") end else Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} does not exist, not updated") end true end def create_key(key_path, recursive) Chef::Log.debug("Creating registry key #{key_path}") if keys_missing?(key_path) if recursive == true Chef::Log.debug("Registry key #{key_path} has missing subkeys, and recursive specified, creating them....") create_missing(key_path) else raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has missing subkeys, and recursive not specified" end end if key_exists?(key_path) Chef::Log.debug("Registry key #{key_path} already exists, doing nothing") else hive, key = get_hive_and_key(key_path) hive.create(key, ::Win32::Registry::KEY_WRITE | registry_system_architecture) Chef::Log.debug("Registry key #{key_path} created") end true end def delete_key(key_path, recursive) Chef::Log.debug("Deleting registry key #{key_path}") unless key_exists?(key_path) Chef::Log.debug("Registry key #{key_path}, does not exist, not deleting") return true end #key_path is in the form "HKLM\Software\Opscode" for example, extracting #hive = HKLM, #hive_namespace = ::Win32::Registry::HKEY_LOCAL_MACHINE hive = key_path.split("\\").shift hive_namespace, key_including_parent = get_hive_and_key(key_path) if has_subkeys?(key_path) if recursive == true subkeys = get_subkeys(key_path) subkeys.each do |key| keypath_to_check = hive+"\\"+key_including_parent+"\\"+key Chef::Log.debug("Deleting registry key #{key_path} recursively") delete_key(keypath_to_check, true) end delete_key_ex(hive_namespace, key_including_parent) else raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified" end else delete_key_ex(hive_namespace, key_including_parent) return true end true end #Using the 'RegDeleteKeyEx' Windows API that correctly supports WOW64 systems (Win2003) #instead of the 'RegDeleteKey' def delete_key_ex(hive, key) regDeleteKeyEx = ::Win32::API.new('RegDeleteKeyEx', 'LPLL', 'L', 'advapi32') hive_num = hive.hkey - (1 << 32) regDeleteKeyEx.call(hive_num, key, ::Win32::Registry::KEY_WRITE | registry_system_architecture, 0) end def key_exists?(key_path) hive, key = get_hive_and_key(key_path) begin hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |current_key| return true end rescue ::Win32::Registry::Error => e return false end end def key_exists!(key_path) unless key_exists?(key_path) raise Chef::Exceptions::Win32RegKeyMissing, "Registry key #{key_path} does not exist" end true end def hive_exists?(key_path) begin hive, key = get_hive_and_key(key_path) rescue Chef::Exceptions::Win32RegHiveMissing => e return false end return true end def has_subkeys?(key_path) key_exists!(key_path) hive, key = get_hive_and_key(key_path) hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| reg.each_key{ |key| return true } end return false end def get_subkeys(key_path) subkeys = [] key_exists!(key_path) hive, key = get_hive_and_key(key_path) hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| reg.each_key{ |current_key| subkeys << current_key } end return subkeys end # 32-bit chef clients running on 64-bit machines will default to reading the 64-bit registry def registry_system_architecture applied_arch = ( architecture == :machine ) ? machine_architecture : architecture ( applied_arch == :x86_64 ) ? 0x0100 : 0x0200 end def value_exists?(key_path, value) key_exists!(key_path) hive, key = get_hive_and_key(key_path) hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| return true if reg.any? {|val| val == value[:name] } end return false end def data_exists?(key_path, value) key_exists!(key_path) hive, key = get_hive_and_key(key_path) hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| reg.each do |val_name, val_type, val_data| if val_name == value[:name] && val_type == get_type_from_name(value[:type]) && val_data == value[:data] return true end end end return false end def value_exists!(key_path, value) unless value_exists?(key_path, value) raise Chef::Exceptions::Win32RegValueMissing, "Registry key #{key_path} has no value named #{value[:name]}" end true end def data_exists!(key_path, value) unless data_exists?(key_path, value) raise Chef::Exceptions::Win32RegDataMissing, "Registry key #{key_path} has no value named #{value[:name]}, containing type #{value[:type]} and data #{value[:data]}" end true end def type_matches?(key_path, value) value_exists!(key_path, value) hive, key = get_hive_and_key(key_path) hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg| reg.each do |val_name, val_type| if val_name == value[:name] type_new = get_type_from_name(value[:type]) if val_type == type_new return true end end end end return false end def type_matches!(key_path, value) unless type_matches?(key_path, value) raise Chef::Exceptions::Win32RegTypesMismatch, "Registry key #{key_path} has a value #{value[:name]} with a type that is not #{value[:type]}" end end def get_type_from_name(val_type) value = { :binary => ::Win32::Registry::REG_BINARY, :string => ::Win32::Registry::REG_SZ, :multi_string => ::Win32::Registry::REG_MULTI_SZ, :expand_string => ::Win32::Registry::REG_EXPAND_SZ, :dword => ::Win32::Registry::REG_DWORD, :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN, :qword => ::Win32::Registry::REG_QWORD }[val_type] return value end def keys_missing?(key_path) missing_key_arr = key_path.split("\\") missing_key_arr.pop key = missing_key_arr.join("\\") !key_exists?(key) end def get_type_from_name(val_type) _type_name_map[val_type] end def get_name_from_type(val_class) _name_type_map[val_class] end private def node run_context && run_context.node end def machine_architecture node[:kernel][:machine].to_sym end def assert_architecture! if machine_architecture == :i386 && architecture == :x86_64 raise Chef::Exceptions::Win32RegArchitectureIncorrect, "cannot access 64-bit registry on a 32-bit windows instance" end end def get_hive_and_key(path) reg_path = path.split("\\") hive_name = reg_path.shift key = reg_path.join("\\") hive = { "HKLM" => ::Win32::Registry::HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE" => ::Win32::Registry::HKEY_LOCAL_MACHINE, "HKU" => ::Win32::Registry::HKEY_USERS, "HKEY_USERS" => ::Win32::Registry::HKEY_USERS, "HKCU" => ::Win32::Registry::HKEY_CURRENT_USER, "HKEY_CURRENT_USER" => ::Win32::Registry::HKEY_CURRENT_USER, "HKCR" => ::Win32::Registry::HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT" => ::Win32::Registry::HKEY_CLASSES_ROOT, "HKCC" => ::Win32::Registry::HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG" => ::Win32::Registry::HKEY_CURRENT_CONFIG, }[hive_name] raise Chef::Exceptions::Win32RegHiveMissing, "Registry Hive #{hive_name} does not exist" unless hive return hive, key end def _type_name_map { :binary => ::Win32::Registry::REG_BINARY, :string => ::Win32::Registry::REG_SZ, :multi_string => ::Win32::Registry::REG_MULTI_SZ, :expand_string => ::Win32::Registry::REG_EXPAND_SZ, :dword => ::Win32::Registry::REG_DWORD, :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN, :qword => ::Win32::Registry::REG_QWORD } end def _name_type_map @_name_type_map ||= _type_name_map.invert end def get_type_from_num(val_type) value = { 3 => ::Win32::Registry::REG_BINARY, 1 => ::Win32::Registry::REG_SZ, 7 => ::Win32::Registry::REG_MULTI_SZ, 2 => ::Win32::Registry::REG_EXPAND_SZ, 4 => ::Win32::Registry::REG_DWORD, 5 => ::Win32::Registry::REG_DWORD_BIG_ENDIAN, 11 => ::Win32::Registry::REG_QWORD }[val_type] return value end def create_missing(key_path) missing_key_arr = key_path.split("\\") hivename = missing_key_arr.shift missing_key_arr.pop existing_key_path = hivename hive, key = get_hive_and_key(key_path) missing_key_arr.each do |intermediate_key| existing_key_path = existing_key_path << "\\" << intermediate_key if !key_exists?(existing_key_path) Chef::Log.debug("Recursively creating registry key #{existing_key_path}") hive.create(get_key(existing_key_path), ::Win32::Registry::KEY_ALL_ACCESS | registry_system_architecture) end end end def get_key(path) reg_path = path.split("\\") hive_name = reg_path.shift key = reg_path.join("\\") end end end end chef-12.3.0/lib/chef/win32/error.rb0000644000004100000410000000510112520074675016632 0ustar www-datawww-data# # Author:: John Keiser () # Copyright:: Copyright 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 'chef/win32/api/error' require 'chef/win32/memory' require 'chef/win32/unicode' require 'chef/exceptions' class Chef module ReservedNames::Win32 class Error include Chef::ReservedNames::Win32::API::Error extend Chef::ReservedNames::Win32::API::Error def self.format_message(message_id = 0, args = {}) flags = args[:flags] || FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY source = args[:source] language_id = args[:language_id] || 0 varargs = args[:varargs] || [:int, 0] buffer = FFI::MemoryPointer.new :pointer num_chars = FormatMessageW(flags | FORMAT_MESSAGE_ALLOCATE_BUFFER, source, message_id, language_id, buffer, 0, *varargs) if num_chars == 0 raise! end # Extract the string begin return buffer.read_pointer.read_wstring(num_chars) ensure Chef::ReservedNames::Win32::Memory.local_free(buffer.read_pointer) end end def self.get_last_error FFI::LastError.error end # Raises the last error. This should only be called by # Win32 API wrapper functions, and then only when wrapped # in an if() statement (since it unconditionally exits) # === Returns # nil::: always returns nil when it does not raise # === Raises # Chef::Exceptions::Win32APIError::: def self.raise!(message = nil) code = get_last_error msg = format_message(code).strip formatted_message = "" formatted_message << message if message formatted_message << "---- Begin Win32 API output ----\n" formatted_message << "System Error Code: #{code}\n" formatted_message << "System Error Message: #{msg}\n" formatted_message << "---- End Win32 API output ----\n" raise Chef::Exceptions::Win32APIError, msg + "\n" + formatted_message end end end end chef-12.3.0/lib/chef/shell_out.rb0000644000004100000410000000054112520074675016540 0ustar www-datawww-datarequire 'mixlib/shellout' class Chef class ShellOut < Mixlib::ShellOut def initialize(*args) Chef::Log.warn("Chef::ShellOut is deprecated, please use Mixlib::ShellOut") called_from = caller[0..3].inject("Called from:\n") {|msg, trace_line| msg << " #{trace_line}\n" } Chef::Log.warn(called_from) super end end end chef-12.3.0/lib/chef/run_context.rb0000644000004100000410000002557612520074675017131 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2008-2010 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_collection' require 'chef/cookbook_version' require 'chef/node' require 'chef/role' require 'chef/log' require 'chef/recipe' require 'chef/run_context/cookbook_compiler' require 'chef/event_dispatch/events_output_stream' class Chef # == Chef::RunContext # Value object that loads and tracks the context of a Chef run class RunContext # Chef::Node object for this run attr_reader :node # Chef::CookbookCollection for this run attr_reader :cookbook_collection # Resource Definitions for this run. Populated when the files in # +definitions/+ are evaluated (this is triggered by #load). attr_reader :definitions ### # These need to be settable so deploy can run a resource_collection # independent of any cookbooks via +recipe_eval+ # The Chef::ResourceCollection for this run. Populated by evaluating # recipes, which is triggered by #load. (See also: CookbookCompiler) attr_accessor :resource_collection # The list of control groups to execute during the audit phase attr_accessor :audits # A Hash containing the immediate notifications triggered by resources # during the converge phase of the chef run. attr_accessor :immediate_notification_collection # A Hash containing the delayed (end of run) notifications triggered by # resources during the converge phase of the chef run. attr_accessor :delayed_notification_collection # Event dispatcher for this run. attr_reader :events # Hash of factoids for a reboot request. attr_reader :reboot_info # Creates a new Chef::RunContext object and populates its fields. This object gets # used by the Chef Server to generate a fully compiled recipe list for a node. # # === Returns # object:: Duh. :) def initialize(node, cookbook_collection, events) @node = node @cookbook_collection = cookbook_collection @resource_collection = Chef::ResourceCollection.new @audits = {} @immediate_notification_collection = Hash.new {|h,k| h[k] = []} @delayed_notification_collection = Hash.new {|h,k| h[k] = []} @definitions = Hash.new @loaded_recipes = {} @loaded_attributes = {} @events = events @reboot_info = {} @node.run_context = self @cookbook_compiler = nil end # Triggers the compile phase of the chef run. Implemented by # Chef::RunContext::CookbookCompiler def load(run_list_expansion) @cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events) @cookbook_compiler.compile end # Adds an immediate notification to the # +immediate_notification_collection+. The notification should be a # Chef::Resource::Notification or duck type. def notifies_immediately(notification) nr = notification.notifying_resource if nr.instance_of?(Chef::Resource) @immediate_notification_collection[nr.name] << notification else @immediate_notification_collection[nr.declared_key] << notification end end # Adds a delayed notification to the +delayed_notification_collection+. The # notification should be a Chef::Resource::Notification or duck type. def notifies_delayed(notification) nr = notification.notifying_resource if nr.instance_of?(Chef::Resource) @delayed_notification_collection[nr.name] << notification else @delayed_notification_collection[nr.declared_key] << notification end end def immediate_notifications(resource) if resource.instance_of?(Chef::Resource) return @immediate_notification_collection[resource.name] else return @immediate_notification_collection[resource.declared_key] end end def delayed_notifications(resource) if resource.instance_of?(Chef::Resource) return @delayed_notification_collection[resource.name] else return @delayed_notification_collection[resource.declared_key] end end # Evaluates the recipes +recipe_names+. Used by DSL::IncludeRecipe def include_recipe(*recipe_names, current_cookbook: nil) result_recipes = Array.new recipe_names.flatten.each do |recipe_name| if result = load_recipe(recipe_name, current_cookbook: current_cookbook) result_recipes << result end end result_recipes end # Evaluates the recipe +recipe_name+. Used by DSL::IncludeRecipe def load_recipe(recipe_name, current_cookbook: nil) Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe") cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook) if unreachable_cookbook?(cookbook_name) # CHEF-4367 Chef::Log.warn(<<-ERROR_MESSAGE) MissingCookbookDependency: Recipe `#{recipe_name}` is not in the run_list, and cookbook '#{cookbook_name}' is not a dependency of any cookbook in the run_list. To load this recipe, first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're including it from in that cookbook's metadata. ERROR_MESSAGE end if loaded_fully_qualified_recipe?(cookbook_name, recipe_short_name) Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.") false else loaded_recipe(cookbook_name, recipe_short_name) node.loaded_recipe(cookbook_name, recipe_short_name) cookbook = cookbook_collection[cookbook_name] cookbook.load_recipe(recipe_short_name, self) end end def load_recipe_file(recipe_file) if !File.exist?(recipe_file) raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}" end Chef::Log.debug("Loading Recipe File #{recipe_file}") recipe = Chef::Recipe.new('@recipe_files', recipe_file, self) recipe.from_file(recipe_file) recipe end # Looks up an attribute file given the +cookbook_name+ and # +attr_file_name+. Used by DSL::IncludeAttribute def resolve_attribute(cookbook_name, attr_file_name) cookbook = cookbook_collection[cookbook_name] raise Chef::Exceptions::CookbookNotFound, "could not find cookbook #{cookbook_name} while loading attribute #{name}" unless cookbook attribute_filename = cookbook.attribute_filenames_by_short_filename[attr_file_name] raise Chef::Exceptions::AttributeNotFound, "could not find filename for attribute #{attr_file_name} in cookbook #{cookbook_name}" unless attribute_filename attribute_filename end # An Array of all recipes that have been loaded. This is stored internally # as a Hash, so ordering is predictable. # # Recipe names are given in fully qualified form, e.g., the recipe "nginx" # will be given as "nginx::default" # # To determine if a particular recipe has been loaded, use #loaded_recipe? def loaded_recipes @loaded_recipes.keys end # An Array of all attributes files that have been loaded. Stored internally # using a Hash, so order is predictable. # # Attribute file names are given in fully qualified form, e.g., # "nginx::default" instead of "nginx". def loaded_attributes @loaded_attributes.keys end def loaded_fully_qualified_recipe?(cookbook, recipe) @loaded_recipes.has_key?("#{cookbook}::#{recipe}") end # Returns true if +recipe+ has been loaded, false otherwise. Default recipe # names are expanded, so `loaded_recipe?("nginx")` and # `loaded_recipe?("nginx::default")` are valid and give identical results. def loaded_recipe?(recipe) cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe) loaded_fully_qualified_recipe?(cookbook, recipe_name) end def loaded_fully_qualified_attribute?(cookbook, attribute_file) @loaded_attributes.has_key?("#{cookbook}::#{attribute_file}") end def loaded_attribute(cookbook, attribute_file) @loaded_attributes["#{cookbook}::#{attribute_file}"] = true end ## # Cookbook File Introspection def has_template_in_cookbook?(cookbook, template_name) cookbook = cookbook_collection[cookbook] cookbook.has_template_for_node?(node, template_name) end def has_cookbook_file_in_cookbook?(cookbook, cb_file_name) cookbook = cookbook_collection[cookbook] cookbook.has_cookbook_file_for_node?(node, cb_file_name) end # Delegates to CookbookCompiler#unreachable_cookbook? # Used to raise an error when attempting to load a recipe belonging to a # cookbook that is not in the dependency graph. See also: CHEF-4367 def unreachable_cookbook?(cookbook_name) @cookbook_compiler.unreachable_cookbook?(cookbook_name) end # Open a stream object that can be printed into and will dispatch to events # # == Arguments # options is a hash with these possible options: # - name: a string that identifies the stream to the user. Preferably short. # # Pass a block and the stream will be yielded to it, and close on its own # at the end of the block. def open_stream(options = {}) stream = EventDispatch::EventsOutputStream.new(events, options) if block_given? begin yield stream ensure stream.close end else stream end end # there are options for how to handle multiple calls to these functions: # 1. first call always wins (never change @reboot_info once set). # 2. last call always wins (happily change @reboot_info whenever). # 3. raise an exception on the first conflict. # 4. disable reboot after this run if anyone ever calls :cancel. # 5. raise an exception on any second call. # 6. ? def request_reboot(reboot_info) Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to #{reboot_info.inspect}" @reboot_info = reboot_info end def cancel_reboot Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to {}" @reboot_info = {} end def reboot_requested? @reboot_info.size > 0 end private def loaded_recipe(cookbook, recipe) @loaded_recipes["#{cookbook}::#{recipe}"] = true end end end chef-12.3.0/lib/chef/workstation_config_loader.rb0000644000004100000410000001311012520074675021775 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/config_fetcher' require 'chef/config' require 'chef/null_logger' require 'chef/util/path_helper' class Chef class WorkstationConfigLoader # Path to a config file requested by user, (e.g., via command line option). Can be nil attr_accessor :explicit_config_file # TODO: initialize this with a logger for Chef and Knife def initialize(explicit_config_file, logger=nil) @explicit_config_file = explicit_config_file @config_location = nil @logger = logger || NullLogger.new end def no_config_found? config_location.nil? end def config_location @config_location ||= (explicit_config_file || locate_local_config) end def chef_config_dir if @chef_config_dir.nil? @chef_config_dir = false full_path = working_directory.split(File::SEPARATOR) (full_path.length - 1).downto(0) do |i| candidate_directory = File.join(full_path[0..i] + [".chef" ]) if File.exist?(candidate_directory) && File.directory?(candidate_directory) @chef_config_dir = candidate_directory break end end end @chef_config_dir end def load # Ignore it if there's no explicit_config_file and can't find one at a # default path. return false if config_location.nil? if explicit_config_file && !path_exists?(config_location) raise Exceptions::ConfigurationError, "Specified config file #{config_location} does not exist" end # Have to set Chef::Config.config_file b/c other config is derived from it. Chef::Config.config_file = config_location read_config(IO.read(config_location), config_location) end # (Private API, public for test purposes) def env ENV end # (Private API, public for test purposes) def path_exists?(path) Pathname.new(path).expand_path.exist? end private def have_config?(path) if path_exists?(path) logger.info("Using config at #{path}") true else logger.debug("Config not found at #{path}, trying next option") false end end def locate_local_config candidate_configs = [] # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine) if env['KNIFE_HOME'] candidate_configs << File.join(env['KNIFE_HOME'], 'config.rb') candidate_configs << File.join(env['KNIFE_HOME'], 'knife.rb') end # Look for $PWD/knife.rb if Dir.pwd candidate_configs << File.join(Dir.pwd, 'config.rb') candidate_configs << File.join(Dir.pwd, 'knife.rb') end # Look for $UPWARD/.chef/knife.rb if chef_config_dir candidate_configs << File.join(chef_config_dir, 'config.rb') candidate_configs << File.join(chef_config_dir, 'knife.rb') end # Look for $HOME/.chef/knife.rb Chef::Util::PathHelper.home('.chef') do |dot_chef_dir| candidate_configs << File.join(dot_chef_dir, 'config.rb') candidate_configs << File.join(dot_chef_dir, 'knife.rb') end candidate_configs.find do | candidate_config | have_config?(candidate_config) end end def working_directory a = if Chef::Platform.windows? env['CD'] else env['PWD'] end || Dir.pwd a end def read_config(config_content, config_file_path) Chef::Config.from_string(config_content, config_file_path) rescue SignalException raise rescue SyntaxError => e message = "" message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n" message << "#{e.class.name}: #{e.message}\n" if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/] line = file_line[/:([\d]+)$/, 1].to_i message << highlight_config_error(config_file_path, line) end raise Exceptions::ConfigurationError, message rescue Exception => e message = "You have an error in your config file #{config_file_path}\n\n" message << "#{e.class.name}: #{e.message}\n" filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/) filtered_trace.each {|bt_line| message << " " << bt_line << "\n" } if !filtered_trace.empty? line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/, 1] message << highlight_config_error(config_file_path, line_nr.to_i) end raise Exceptions::ConfigurationError, message end def highlight_config_error(file, line) config_file_lines = [] IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"} if line == 1 lines = config_file_lines[0..3] else lines = config_file_lines[Range.new(line - 2, line)] end "Relevant file content:\n" + lines.join("\n") + "\n" end def logger @logger end end end chef-12.3.0/lib/chef/chef_fs.rb0000644000004100000410000000017212520074675016137 0ustar www-datawww-datarequire 'chef/platform' class Chef module ChefFS def self.windows? Chef::Platform.windows? end end end chef-12.3.0/lib/chef/resource_builder.rb0000644000004100000410000001215612520074675020104 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 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. # # NOTE: this was extracted from the Recipe DSL mixin, relevant specs are in spec/unit/recipe_spec.rb class Chef class ResourceBuilder attr_reader :type attr_reader :name attr_reader :created_at attr_reader :params attr_reader :run_context attr_reader :cookbook_name attr_reader :recipe_name attr_reader :enclosing_provider attr_reader :resource # FIXME (ruby-2.1 syntax): most of these are mandatory def initialize(type:nil, name:nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil) @type = type @name = name @created_at = created_at @params = params @run_context = run_context @cookbook_name = cookbook_name @recipe_name = recipe_name @enclosing_provider = enclosing_provider end def build(&block) raise ArgumentError, "You must supply a name when declaring a #{type} resource" if name.nil? @resource = resource_class.new(name, run_context) resource.source_line = created_at resource.declared_type = type # If we have a resource like this one, we want to steal its state # This behavior is very counter-intuitive and should be removed. # See CHEF-3694, https://tickets.opscode.com/browse/CHEF-3694 # Moved to this location to resolve CHEF-5052, https://tickets.opscode.com/browse/CHEF-5052 if prior_resource resource.load_from(prior_resource) end resource.cookbook_name = cookbook_name resource.recipe_name = recipe_name # Determine whether this resource is being created in the context of an enclosing Provider resource.enclosing_provider = enclosing_provider # XXX: this is required for definition params inside of the scope of a # subresource to work correctly. resource.params = params # Evaluate resource attribute DSL resource.instance_eval(&block) if block_given? # emit a cloned resource warning if it is warranted if prior_resource if is_trivial_resource?(prior_resource) && identicalish_resources?(prior_resource, resource) emit_harmless_cloning_debug else emit_cloned_resource_warning end end # Run optional resource hook resource.after_created resource end private def resource_class # Checks the new platform => short_name => resource mapping initially # then fall back to the older approach (Chef::Resource.const_get) for # backward compatibility @resource_class ||= Chef::Resource.resource_for_node(type, run_context.node) end def is_trivial_resource?(resource) identicalish_resources?(resource_class.new(name, run_context), resource) end # this is an equality test specific to checking for 3694 cloning warnings def identicalish_resources?(first, second) skipped_ivars = [ :@source_line, :@cookbook_name, :@recipe_name, :@params, :@elapsed_time, :@declared_type ] checked_ivars = ( first.instance_variables | second.instance_variables ) - skipped_ivars non_matching_ivars = checked_ivars.reject do |iv| if iv == :@action && ( [first.instance_variable_get(iv)].flatten == [:nothing] || [second.instance_variable_get(iv)].flatten == [:nothing] ) # :nothing action on either side of the comparison always matches true else first.instance_variable_get(iv) == second.instance_variable_get(iv) end end Chef::Log.debug("ivars which did not match with the prior resource: #{non_matching_ivars}") non_matching_ivars.empty? end def emit_cloned_resource_warning Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)") Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line Chef::Log.warn("Current #{resource}: #{resource.source_line}") if resource.source_line end def emit_harmless_cloning_debug Chef::Log.debug("Harmless resource cloning from #{prior_resource}:#{prior_resource.source_line} to #{resource}:#{resource.source_line}") end def prior_resource @prior_resource ||= begin key = "#{type}[#{name}]" prior_resource = run_context.resource_collection.lookup(key) rescue Chef::Exceptions::ResourceNotFound nil end end end end chef-12.3.0/lib/chef/monologger.rb0000644000004100000410000000363512520074675016721 0ustar www-datawww-datarequire '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) @filename = log end @dev.sync = true 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)) 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 chef-12.3.0/lib/chef/cookbook/0000755000004100000410000000000012520074675016023 5ustar www-datawww-datachef-12.3.0/lib/chef/cookbook/chefignore.rb0000644000004100000410000000443112520074675020463 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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. # class Chef class Cookbook class Chefignore COMMENTS_AND_WHITESPACE = /^\s*(?:#.*)?$/ attr_reader :ignores def initialize(ignore_file_or_repo) # Check the 'ignore_file_or_repo' path first and then look in the parent directory # to handle both the chef repo cookbook layout and a standalone cookbook @ignore_file = find_ignore_file(ignore_file_or_repo) @ignore_file = find_ignore_file(File.dirname(ignore_file_or_repo)) unless readable_file_or_symlink?(@ignore_file) @ignores = parse_ignore_file end def remove_ignores_from(file_list) Array(file_list).inject([]) do |unignored, file| ignored?(file) ? unignored : unignored << file end end def ignored?(file_name) @ignores.any? {|glob| File.fnmatch?(glob, file_name)} end private def parse_ignore_file ignore_globs = [] if readable_file_or_symlink?(@ignore_file) File.foreach(@ignore_file) do |line| ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE end else Chef::Log.debug("No chefignore file found at #@ignore_file no files will be ignored") end ignore_globs end def find_ignore_file(path) if File.basename(path) =~ /chefignore/ path else File.join(path, 'chefignore') end end def readable_file_or_symlink?(path) File.exist?(@ignore_file) && File.readable?(@ignore_file) && (File.file?(@ignore_file) || File.symlink?(@ignore_file)) end end end end chef-12.3.0/lib/chef/cookbook/metadata.rb0000644000004100000410000006577312520074675020152 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen () # Author:: Seth Falcon () # Copyright:: Copyright 2008-2010 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 'chef/mash' require 'chef/mixin/from_file' require 'chef/mixin/params_validate' require 'chef/log' require 'chef/version_class' require 'chef/version_constraint' require 'chef/json_compat' class Chef class Cookbook # == Chef::Cookbook::Metadata # Chef::Cookbook::Metadata provides a convenient DSL for declaring metadata # about Chef Cookbooks. class Metadata NAME = 'name'.freeze DESCRIPTION = 'description'.freeze LONG_DESCRIPTION = 'long_description'.freeze MAINTAINER = 'maintainer'.freeze MAINTAINER_EMAIL = 'maintainer_email'.freeze LICENSE = 'license'.freeze PLATFORMS = 'platforms'.freeze DEPENDENCIES = 'dependencies'.freeze RECOMMENDATIONS = 'recommendations'.freeze SUGGESTIONS = 'suggestions'.freeze CONFLICTING = 'conflicting'.freeze PROVIDING = 'providing'.freeze REPLACING = 'replacing'.freeze ATTRIBUTES = 'attributes'.freeze GROUPINGS = 'groupings'.freeze RECIPES = 'recipes'.freeze VERSION = 'version'.freeze SOURCE_URL = 'source_url'.freeze ISSUES_URL = 'issues_url'.freeze COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer, :maintainer_email, :license, :platforms, :dependencies, :recommendations, :suggestions, :conflicting, :providing, :replacing, :attributes, :groupings, :recipes, :version, :source_url, :issues_url ] VERSION_CONSTRAINTS = {:depends => DEPENDENCIES, :recommends => RECOMMENDATIONS, :suggests => SUGGESTIONS, :conflicts => CONFLICTING, :provides => PROVIDING, :replaces => REPLACING } include Chef::Mixin::ParamsValidate include Chef::Mixin::FromFile attr_reader :platforms attr_reader :dependencies attr_reader :recommendations attr_reader :suggestions attr_reader :conflicting attr_reader :providing attr_reader :replacing attr_reader :attributes attr_reader :groupings attr_reader :recipes attr_reader :version # Builds a new Chef::Cookbook::Metadata object. # # === Parameters # cookbook:: An optional cookbook object # maintainer:: An optional maintainer # maintainer_email:: An optional maintainer email # license::An optional license. Default is Apache v2.0 # # === Returns # metadata def initialize @name = nil @description = '' @long_description = '' @license = 'All rights reserved' @maintainer = nil @maintainer_email = nil @platforms = Mash.new @dependencies = Mash.new @recommendations = Mash.new @suggestions = Mash.new @conflicting = Mash.new @providing = Mash.new @replacing = Mash.new @attributes = Mash.new @groupings = Mash.new @recipes = Mash.new @version = Version.new("0.0.0") @source_url = '' @issues_url = '' @errors = [] end def ==(other) COMPARISON_FIELDS.inject(true) do |equal_so_far, field| equal_so_far && other.respond_to?(field) && (other.send(field) == send(field)) end end # Whether this metadata is valid. In order to be valid, all required # fields must be set. Chef's validation implementation checks the content # of a given field when setting (and raises an error if the content does # not meet the criteria), so the content of the fields is not considered # when checking validity. # # === Returns # valid:: Whether this metadata object is valid def valid? run_validation @errors.empty? end # A list of validation errors for this metadata object. See #valid? for # comments about the validation criteria. # # If there are any validation errors, one or more error strings will be # returned. Otherwise an empty array is returned. # # === Returns # error messages:: Whether this metadata object is valid def errors run_validation @errors end # Sets the cookbooks maintainer, or returns it. # # === Parameters # maintainer:: The maintainers name # # === Returns # maintainer:: Returns the current maintainer. def maintainer(arg=nil) set_or_return( :maintainer, arg, :kind_of => [ String ] ) end # Sets the maintainers email address, or returns it. # # === Parameters # maintainer_email:: The maintainers email address # # === Returns # maintainer_email:: Returns the current maintainer email. def maintainer_email(arg=nil) set_or_return( :maintainer_email, arg, :kind_of => [ String ] ) end # Sets the current license, or returns it. # # === Parameters # license:: The current license. # # === Returns # license:: Returns the current license def license(arg=nil) set_or_return( :license, arg, :kind_of => [ String ] ) end # Sets the current description, or returns it. Should be short - one line only! # # === Parameters # description:: The new description # # === Returns # description:: Returns the description def description(arg=nil) set_or_return( :description, arg, :kind_of => [ String ] ) end # Sets the current long description, or returns it. Might come from a README, say. # # === Parameters # long_description:: The new long description # # === Returns # long_description:: Returns the long description def long_description(arg=nil) set_or_return( :long_description, arg, :kind_of => [ String ] ) end # Sets the current cookbook version, or returns it. Can be two or three digits, separated # by dots. ie: '2.1', '1.5.4' or '0.9'. # # === Parameters # version:: The current version, as a string # # === Returns # version:: Returns the current version def version(arg=nil) if arg @version = Chef::Version.new(arg) end @version.to_s end # Sets the name of the cookbook, or returns it. # # === Parameters # name:: The current cookbook name. # # === Returns # name:: Returns the current cookbook name. def name(arg=nil) set_or_return( :name, arg, :kind_of => [ String ] ) end # Adds a supported platform, with version checking strings. # # === Parameters # platform,:: The platform (like :ubuntu or :mac_os_x) # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has # the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def supports(platform, *version_args) version = new_args_format(:supports, platform, version_args) constraint = validate_version_constraint(:supports, platform, version) @platforms[platform] = constraint.to_s @platforms[platform] end # Adds a dependency on another cookbook, with version checking strings. # # === Parameters # cookbook:: The cookbook # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has # the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def depends(cookbook, *version_args) version = new_args_format(:depends, cookbook, version_args) constraint = validate_version_constraint(:depends, cookbook, version) @dependencies[cookbook] = constraint.to_s @dependencies[cookbook] end # Adds a recommendation for another cookbook, with version checking strings. # # === Parameters # cookbook:: The cookbook # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has # the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def recommends(cookbook, *version_args) version = new_args_format(:recommends, cookbook, version_args) constraint = validate_version_constraint(:recommends, cookbook, version) @recommendations[cookbook] = constraint.to_s @recommendations[cookbook] end # Adds a suggestion for another cookbook, with version checking strings. # # === Parameters # cookbook:: The cookbook # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has the # formx.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def suggests(cookbook, *version_args) version = new_args_format(:suggests, cookbook, version_args) constraint = validate_version_constraint(:suggests, cookbook, version) @suggestions[cookbook] = constraint.to_s @suggestions[cookbook] end # Adds a conflict for another cookbook, with version checking strings. # # === Parameters # cookbook:: The cookbook # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has # the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def conflicts(cookbook, *version_args) version = new_args_format(:conflicts, cookbook, version_args) constraint = validate_version_constraint(:conflicts, cookbook, version) @conflicting[cookbook] = constraint.to_s @conflicting[cookbook] end # Adds a recipe, definition, or resource provided by this cookbook. # # Recipes are specified as normal # Definitions are followed by (), and can include :params for prototyping # Resources are the stringified version (service[apache2]) # # === Parameters # recipe, definition, resource:: The thing we provide # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has # the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def provides(cookbook, *version_args) version = new_args_format(:provides, cookbook, version_args) constraint = validate_version_constraint(:provides, cookbook, version) @providing[cookbook] = constraint.to_s @providing[cookbook] end # Adds a cookbook that is replaced by this one, with version checking strings. # # === Parameters # cookbook:: The cookbook we replace # version:: A version constraint of the form "OP VERSION", # where OP is one of < <= = > >= ~> and VERSION has the form x.y.z or x.y. # # === Returns # versions:: Returns the list of versions for the platform def replaces(cookbook, *version_args) version = new_args_format(:replaces, cookbook, version_args) constraint = validate_version_constraint(:replaces, cookbook, version) @replacing[cookbook] = constraint.to_s @replacing[cookbook] end # Adds a description for a recipe. # # === Parameters # recipe:: The recipe # description:: The description of the recipe # # === Returns # description:: Returns the current description def recipe(name, description) @recipes[name] = description end # Sets the cookbook's recipes to the list of recipes in the given # +cookbook+. Any recipe that already has a description (if set by the # #recipe method) will not be updated. # # === Parameters # cookbook:: CookbookVersion object representing the cookbook # description:: The description of the recipe # # === Returns # recipe_unqualified_names:: An array of the recipe names given by the cookbook def recipes_from_cookbook_version(cookbook) cookbook.fully_qualified_recipe_names.map do |recipe_name| unqualified_name = if recipe_name =~ /::default$/ self.name.to_s else recipe_name end @recipes[unqualified_name] ||= "" provides(unqualified_name) unqualified_name end end # Adds an attribute that a user needs to configure for this cookbook. Takes # a name (with the / notation for a nested attribute), followed by any of # these options # # display_name:: What a UI should show for this attribute # description:: A hint as to what this attr is for # choice:: An array of choices to present to the user. # calculated:: If true, the default value is calculated by the recipe and cannot be displayed. # type:: "string" or "array" - default is "string" ("hash" is supported for backwards compatibility) # required:: Whether this attr is 'required', 'recommended' or 'optional' - default 'optional' (true/false values also supported for backwards compatibility) # recipes:: An array of recipes which need this attr set. # default,,:: The default value # # === Parameters # name:: The name of the attribute ('foo', or 'apache2/log_dir') # options:: The description of the options # # === Returns # options:: Returns the current options hash def attribute(name, options) validate( options, { :display_name => { :kind_of => String }, :description => { :kind_of => String }, :choice => { :kind_of => [ Array ], :default => [] }, :calculated => { :equal_to => [ true, false ], :default => false }, :type => { :equal_to => [ "string", "array", "hash", "symbol", "boolean", "numeric" ], :default => "string" }, :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" }, :recipes => { :kind_of => [ Array ], :default => [] }, :default => { :kind_of => [ String, Array, Hash, Symbol, Numeric, TrueClass, FalseClass ] }, :source_url => { :kind_of => String }, :issues_url => { :kind_of => String } } ) options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil? validate_choice_array(options) validate_calculated_default_rule(options) validate_choice_default_rule(options) @attributes[name] = options @attributes[name] end def grouping(name, options) validate( options, { :title => { :kind_of => String }, :description => { :kind_of => String } } ) @groupings[name] = options @groupings[name] end def to_hash { NAME => self.name, DESCRIPTION => self.description, LONG_DESCRIPTION => self.long_description, MAINTAINER => self.maintainer, MAINTAINER_EMAIL => self.maintainer_email, LICENSE => self.license, PLATFORMS => self.platforms, DEPENDENCIES => self.dependencies, RECOMMENDATIONS => self.recommendations, SUGGESTIONS => self.suggestions, CONFLICTING => self.conflicting, PROVIDING => self.providing, REPLACING => self.replacing, ATTRIBUTES => self.attributes, GROUPINGS => self.groupings, RECIPES => self.recipes, VERSION => self.version, SOURCE_URL => self.source_url, ISSUES_URL => self.issues_url } end def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def self.from_hash(o) cm = self.new() cm.from_hash(o) cm end def from_hash(o) @name = o[NAME] if o.has_key?(NAME) @description = o[DESCRIPTION] if o.has_key?(DESCRIPTION) @long_description = o[LONG_DESCRIPTION] if o.has_key?(LONG_DESCRIPTION) @maintainer = o[MAINTAINER] if o.has_key?(MAINTAINER) @maintainer_email = o[MAINTAINER_EMAIL] if o.has_key?(MAINTAINER_EMAIL) @license = o[LICENSE] if o.has_key?(LICENSE) @platforms = o[PLATFORMS] if o.has_key?(PLATFORMS) @dependencies = handle_deprecated_constraints(o[DEPENDENCIES]) if o.has_key?(DEPENDENCIES) @recommendations = handle_deprecated_constraints(o[RECOMMENDATIONS]) if o.has_key?(RECOMMENDATIONS) @suggestions = handle_deprecated_constraints(o[SUGGESTIONS]) if o.has_key?(SUGGESTIONS) @conflicting = handle_deprecated_constraints(o[CONFLICTING]) if o.has_key?(CONFLICTING) @providing = o[PROVIDING] if o.has_key?(PROVIDING) @replacing = handle_deprecated_constraints(o[REPLACING]) if o.has_key?(REPLACING) @attributes = o[ATTRIBUTES] if o.has_key?(ATTRIBUTES) @groupings = o[GROUPINGS] if o.has_key?(GROUPINGS) @recipes = o[RECIPES] if o.has_key?(RECIPES) @version = o[VERSION] if o.has_key?(VERSION) @source_url = o[SOURCE_URL] if o.has_key?(SOURCE_URL) @issues_url = o[ISSUES_URL] if o.has_key?(ISSUES_URL) self end def self.from_json(string) o = Chef::JSONCompat.from_json(string) self.from_hash(o) end def self.validate_json(json_str) o = Chef::JSONCompat.from_json(json_str) metadata = new() VERSION_CONSTRAINTS.each do |dependency_type, hash_key| if dependency_group = o[hash_key] dependency_group.each do |cb_name, constraints| if metadata.respond_to?(method_name) metadata.public_send(method_name, cb_name, *Array(constraints)) end end end end true end def from_json(string) o = Chef::JSONCompat.from_json(string) from_hash(o) end # Sets the cookbook's source URL, or returns it. # # === Parameters # maintainer:: The source URL # # === Returns # source_url:: Returns the current source URL. def source_url(arg=nil) set_or_return( :source_url, arg, :kind_of => [ String ] ) end # Sets the cookbook's issues URL, or returns it. # # === Parameters # issues_url:: The issues URL # # === Returns # issues_url:: Returns the current issues URL. def issues_url(arg=nil) set_or_return( :issues_url, arg, :kind_of => [ String ] ) end private def run_validation if name.nil? @errors = ["The `name' attribute is required in cookbook metadata"] end end def new_args_format(caller_name, dep_name, version_constraints) if version_constraints.empty? ">= 0.0.0" elsif version_constraints.size == 1 version_constraints.first else msg=<<-OBSOLETED The dependency specification syntax you are using is no longer valid. You may not specify more than one version constraint for a particular cookbook. Consult http://wiki.opscode.com/display/chef/Metadata for the updated syntax. Called by: #{caller_name} '#{dep_name}', #{version_constraints.map {|vc| vc.inspect}.join(", ")} Called from: #{caller[0...5].map {|line| " " + line}.join("\n")} OBSOLETED raise Exceptions::ObsoleteDependencySyntax, msg end end def validate_version_constraint(caller_name, dep_name, constraint_str) Chef::VersionConstraint.new(constraint_str) rescue Chef::Exceptions::InvalidVersionConstraint => e Log.debug(e) msg=<<-INVALID The version constraint syntax you are using is not valid. If you recently upgraded to Chef 0.10.0, be aware that you no may longer use "<<" and ">>" for 'less than' and 'greater than'; use '<' and '>' instead. Consult http://wiki.opscode.com/display/chef/Metadata for more information. Called by: #{caller_name} '#{dep_name}', '#{constraint_str}' Called from: #{caller[0...5].map {|line| " " + line}.join("\n")} INVALID raise Exceptions::InvalidVersionConstraint, msg end # Verify that the given array is an array of strings # # Raise an exception if the members of the array are not Strings # # === Parameters # arry:: An array to be validated def validate_string_array(arry) if arry.kind_of?(Array) arry.each do |choice| validate( {:choice => choice}, {:choice => {:kind_of => String}} ) end end end # Validate the choice of the options hash # # Raise an exception if the members of the array do not match the defaults # === Parameters # opts:: The options hash def validate_choice_array(opts) if opts[:choice].kind_of?(Array) case opts[:type] when "string" validator = [ String ] when "array" validator = [ Array ] when "hash" validator = [ Hash ] when "symbol" validator = [ Symbol ] when "boolean" validator = [ TrueClass, FalseClass ] when "numeric" validator = [ Numeric ] end opts[:choice].each do |choice| validate( {:choice => choice}, {:choice => {:kind_of => validator}} ) end end end # For backwards compatibility, remap Boolean values to String # true is mapped to "required" # false is mapped to "optional" # # === Parameters # required_attr:: The value of options[:required] # # === Returns # required_attr:: "required", "recommended", or "optional" def remap_required_attribute(value) case value when true value = "required" when false value = "optional" end value end def validate_calculated_default_rule(options) calculated_conflict = ((options[:default].is_a?(Array) && !options[:default].empty?) || (options[:default].is_a?(String) && !options[:default] != "")) && options[:calculated] == true raise ArgumentError, "Default cannot be specified if calculated is true!" if calculated_conflict end def validate_choice_default_rule(options) return if !options[:choice].is_a?(Array) || options[:choice].empty? if options[:default].is_a?(String) && options[:default] != "" raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]) == nil end if options[:default].is_a?(Array) && !options[:default].empty? options[:default].each do |val| raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val) == nil end end end # This method translates version constraint strings from # cookbooks with the old format. # # Before we began respecting version constraints, we allowed # multiple constraints to be placed on cookbooks, as well as the # << and >> operators, which are now just < and >. For # specifications with more than one constraint, we return an # empty array (otherwise, we're silently abiding only part of # the contract they have specified to us). If there is only one # constraint, we are replacing the old << and >> with the new < # and >. def handle_deprecated_constraints(specification) specification.inject(Mash.new) do |acc, (cb, constraints)| constraints = Array(constraints) acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first.gsub(/>>/, '>').gsub(/<) # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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/cookbook/file_vendor' class Chef class Cookbook # == Chef::Cookbook::FileSystemFileVendor # This FileVendor loads files from Chef::Config.cookbook_path. The # thing that's sort of janky about this FileVendor implementation is # that it basically takes only the cookbook's name from the manifest # and throws the rest away then re-builds the list of files on the # disk. This is due to the manifest not having the on-disk file # locations, since in the chef-client case, that information is # non-sensical. class FileSystemFileVendor < FileVendor attr_reader :cookbook_name attr_reader :repo_paths def initialize(manifest, *repo_paths) @cookbook_name = manifest[:cookbook_name] @repo_paths = repo_paths.flatten raise ArgumentError, "You must specify at least one repo path" if @repo_paths.empty? end # Implements abstract base's requirement. It looks in the # Chef::Config.cookbook_path file hierarchy for the requested # file. def get_filename(filename) location = @repo_paths.inject(nil) do |memo, basepath| candidate_location = File.join(basepath, @cookbook_name, filename) memo = candidate_location if File.exist?(candidate_location) memo end raise "File #{filename} does not exist for cookbook #{@cookbook_name}" unless location location end end end end chef-12.3.0/lib/chef/cookbook/synchronizer.rb0000644000004100000410000002161212520074675021107 0ustar www-datawww-datarequire 'chef/client' require 'chef/util/threaded_job_queue' require 'singleton' class Chef # Keep track of the filenames that we use in both eager cookbook # downloading (during sync_cookbooks) and lazy (during the run # itself, through FileVendor). After the run is over, clean up the # cache. class CookbookCacheCleaner attr_accessor :skip_removal # Setup a notification to clear the valid_cache_entries when a Chef client # run starts Chef::Client.when_run_starts do |run_status| instance.reset! end # Register a notification to cleanup unused files from cookbooks Chef::Client.when_run_completes_successfully do |run_status| instance.cleanup_file_cache end include Singleton def initialize reset! end def reset! @valid_cache_entries = {} end def mark_file_as_valid(cache_path) @valid_cache_entries[cache_path] = true end def cache Chef::FileCache end def cleanup_file_cache unless Chef::Config[:solo] || skip_removal # Delete each file in the cache that we didn't encounter in the # manifest. cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename| unless @valid_cache_entries[cache_filename] Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.") cache.delete(cache_filename) end end else Chef::Log.info("Skipping removal of unused files from the cache") end end end # Synchronizes the locally cached copies of cookbooks with the files on the # server. class CookbookSynchronizer CookbookFile = Struct.new(:cookbook, :segment, :manifest_record) attr_accessor :remove_obsoleted_files def initialize(cookbooks_by_name, events) @eager_segments = Chef::CookbookVersion::COOKBOOK_SEGMENTS.dup unless Chef::Config[:no_lazy_load] @eager_segments.delete(:files) @eager_segments.delete(:templates) end @eager_segments.freeze @cookbooks_by_name, @events = cookbooks_by_name, events @cookbook_full_file_paths = {} @remove_obsoleted_files = true end def cache Chef::FileCache end def cookbook_names @cookbooks_by_name.keys end def cookbooks @cookbooks_by_name.values end def cookbook_count @cookbooks_by_name.size end def have_cookbook?(cookbook_name) @cookbooks_by_name.key?(cookbook_name) end def cookbook_segment(cookbook_name, segment) @cookbooks_by_name[cookbook_name].manifest[segment] end def files @files ||= cookbooks.inject([]) do |memo, cookbook| @eager_segments.each do |segment| cookbook.manifest[segment].each do |manifest_record| memo << CookbookFile.new(cookbook, segment, manifest_record) end end memo end end def files_by_cookbook files.group_by { |file| file.cookbook } end def files_remaining_by_cookbook @files_remaining_by_cookbook ||= begin files_by_cookbook.inject({}) do |memo, (cookbook, files)| memo[cookbook] = files.size memo end end end def mark_file_synced(file) files_remaining_by_cookbook[file.cookbook] -= 1 if files_remaining_by_cookbook[file.cookbook] == 0 @events.synchronized_cookbook(file.cookbook.name) end end # Synchronizes all the cookbooks from the chef-server. #) # === Returns # true:: Always returns true def sync_cookbooks Chef::Log.info("Loading cookbooks [#{cookbooks.map {|ckbk| ckbk.name + '@' + ckbk.version}.join(', ')}]") Chef::Log.debug("Cookbooks detail: #{cookbooks.inspect}") clear_obsoleted_cookbooks queue = Chef::Util::ThreadedJobQueue.new files.each do |file| queue << lambda do |lock| full_file_path = sync_file(file) lock.synchronize { # Save the full_path of the downloaded file to be restored in the manifest later save_full_file_path(file, full_file_path) mark_file_synced(file) } end end @events.cookbook_sync_start(cookbook_count) queue.process(Chef::Config[:cookbook_sync_threads]) # Update the full file paths in the manifest update_cookbook_filenames() rescue Exception => e @events.cookbook_sync_failed(cookbooks, e) raise else @events.cookbook_sync_complete true end # Saves the full_path to the file of the cookbook to be updated # in the manifest later def save_full_file_path(file, full_path) @cookbook_full_file_paths[file.cookbook] ||= { } @cookbook_full_file_paths[file.cookbook][file.segment] ||= [ ] @cookbook_full_file_paths[file.cookbook][file.segment] << full_path end # remove cookbooks that are not referenced in the expanded run_list at all # (if we have an override run_list we may not want to do this) def remove_old_cookbooks cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| cache_file =~ /^cookbooks\/([^\/]+)\// unless have_cookbook?($1) Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.") cache.delete(cache_file) @events.removed_cookbook_file(cache_file) end end end # remove deleted files in cookbooks that are being used on the node def remove_deleted_files cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| md = cache_file.match(/^cookbooks\/([^\/]+)\/([^\/]+)\/(.*)/) next unless md ( cookbook_name, segment, file ) = md[1..3] if have_cookbook?(cookbook_name) manifest_segment = cookbook_segment(cookbook_name, segment) if manifest_segment.select { |manifest_record| manifest_record["path"] == "#{segment}/#{file}" }.empty? Chef::Log.info("Removing #{cache_file} from the cache; its is no longer in the cookbook manifest.") cache.delete(cache_file) @events.removed_cookbook_file(cache_file) end end end end # Iterates over cached cookbooks' files, removing files belonging to # cookbooks that don't appear in +cookbook_hash+ def clear_obsoleted_cookbooks @events.cookbook_clean_start if remove_obsoleted_files remove_old_cookbooks else Chef::Log.info("Skipping removal of obsoleted cookbooks from the cache") CookbookCacheCleaner.instance.skip_removal = true end remove_deleted_files @events.cookbook_clean_complete end def update_cookbook_filenames @cookbook_full_file_paths.each do |cookbook, file_segments| file_segments.each do |segment, full_paths| cookbook.replace_segment_filenames(segment, full_paths) end end end # Sync an individual file if needed. If there is an up to date copy # locally, nothing is done. Updates +file+'s manifest with the full path to # the cached file. # # === Arguments # file # === Returns # Full path to the cached file as a String def sync_file(file) cache_filename = File.join("cookbooks", file.cookbook.name, file.manifest_record['path']) mark_cached_file_valid(cache_filename) # If the checksums are different between on-disk (current) and on-server # (remote, per manifest), do the update. This will also execute if there # is no current checksum. if !cached_copy_up_to_date?(cache_filename, file.manifest_record['checksum']) download_file(file.manifest_record['url'], cache_filename) @events.updated_cookbook_file(file.cookbook.name, cache_filename) else Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.") end # Load the file in the cache and return the full file path to the loaded file cache.load(cache_filename, false) end def cached_copy_up_to_date?(local_path, expected_checksum) if cache.has_key?(local_path) current_checksum = CookbookVersion.checksum_cookbook_file(cache.load(local_path, false)) expected_checksum == current_checksum else false end end # Unconditionally download the file from the given URL. File will be # downloaded to the path +destination+ which is relative to the Chef file # cache root. def download_file(url, destination) raw_file = server_api.get_rest(url, true) Chef::Log.info("Storing updated #{destination} in the cache.") cache.move_to(raw_file.path, destination) end # Marks the given file as valid (non-stale). def mark_cached_file_valid(cache_filename) CookbookCacheCleaner.instance.mark_file_as_valid(cache_filename) end def server_api Chef::REST.new(Chef::Config[:chef_server_url]) end end end chef-12.3.0/lib/chef/cookbook/syntax_check.rb0000644000004100000410000002123512520074675021036 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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 'pathname' require 'stringio' require 'erubis' require 'chef/mixin/shell_out' require 'chef/mixin/checksum' require 'chef/util/path_helper' class Chef class Cookbook # == Chef::Cookbook::SyntaxCheck # Encapsulates the process of validating the ruby syntax of files in Chef # cookbooks. class SyntaxCheck # == Chef::Cookbook::SyntaxCheck::PersistentSet # Implements set behavior with disk-based persistence. Objects in the set # are expected to be strings containing only characters that are valid in # filenames. # # This class is used to track which files have been syntax checked so # that known good files are not rechecked. class PersistentSet attr_reader :cache_path # Create a new PersistentSet. Values in the set are persisted by # creating a file in the +cache_path+ directory. def initialize(cache_path=Chef::Config[:syntax_check_cache_path]) @cache_path = cache_path @cache_path_created = false end # Adds +value+ to the set's collection. def add(value) ensure_cache_path_created FileUtils.touch(File.join(cache_path, value)) end # Returns true if the set includes +value+ def include?(value) File.exist?(File.join(cache_path, value)) end private def ensure_cache_path_created return true if @cache_path_created FileUtils.mkdir_p(cache_path) @cache_path_created = true end end include Chef::Mixin::ShellOut include Chef::Mixin::Checksum attr_reader :cookbook_path # A PersistentSet object that tracks which files have already been # validated. attr_reader :validated_files attr_reader :chefignore # Creates a new SyntaxCheck given the +cookbook_name+ and a +cookbook_path+. # If no +cookbook_path+ is given, +Chef::Config.cookbook_path+ is used. def self.for_cookbook(cookbook_name, cookbook_path=nil) cookbook_path ||= Chef::Config.cookbook_path unless cookbook_path raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given" end new(File.join(cookbook_path, cookbook_name.to_s)) end # Create a new SyntaxCheck object # === Arguments # cookbook_path::: the (on disk) path to the cookbook def initialize(cookbook_path) @cookbook_path = cookbook_path @chefignore ||= Chefignore.new(cookbook_path) @validated_files = PersistentSet.new end def remove_ignored_files(file_list) return file_list if chefignore.ignores.empty? file_list.reject do |full_path| relative_pn = Chef::Util::PathHelper.relative_path_from(cookbook_path, full_path) chefignore.ignored?(relative_pn.to_s) end end def remove_uninteresting_ruby_files(file_list) file_list.reject { |f| f =~ %r{#{cookbook_path}/(files|templates)/} } end def ruby_files path = Chef::Util::PathHelper.escape_glob(cookbook_path) files = Dir[File.join(path, '**', '*.rb')] files = remove_ignored_files(files) files = remove_uninteresting_ruby_files(files) files end def untested_ruby_files ruby_files.reject do |file| if validated?(file) Chef::Log.debug("Ruby file #{file} is unchanged, skipping syntax check") true else false end end end def template_files remove_ignored_files Dir[File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), '**/templates/**', '*.erb')] end def untested_template_files template_files.reject do |file| if validated?(file) Chef::Log.debug("Template #{file} is unchanged, skipping syntax check") true else false end end end def validated?(file) validated_files.include?(checksum(file)) end def validated(file) validated_files.add(checksum(file)) end def validate_ruby_files untested_ruby_files.each do |ruby_file| return false unless validate_ruby_file(ruby_file) validated(ruby_file) end end def validate_templates untested_template_files.each do |template| return false unless validate_template(template) validated(template) end end def validate_template(erb_file) Chef::Log.debug("Testing template #{erb_file} for syntax errors...") validate_erb_file_inline(erb_file) end def validate_ruby_file(ruby_file) Chef::Log.debug("Testing #{ruby_file} for syntax errors...") validate_ruby_file_inline(ruby_file) end # Validate the ruby code in an erb template. Uses RubyVM to do syntax # checking. def validate_erb_file_inline(erb_file) old_stderr = $stderr engine = Erubis::Eruby.new engine.convert!(IO.read(erb_file)) ruby_code = engine.src # Even when we're compiling the code w/ RubyVM, syntax errors just # print to $stderr. We want to capture this and handle the printing # ourselves, so we must temporarily swap $stderr to capture the output. tmp_stderr = $stderr = StringIO.new abs_path = File.expand_path(erb_file) RubyVM::InstructionSequence.new(ruby_code, erb_file, abs_path, 0) true rescue SyntaxError $stderr = old_stderr invalid_erb_file(erb_file, tmp_stderr.string) false ensure # be paranoid about setting stderr back to the old value. $stderr = old_stderr if defined?(old_stderr) && old_stderr end # Debug a syntax error in a template. def invalid_erb_file(erb_file, error_message) file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:") error_message.each_line { |l| Chef::Log.fatal(l.chomp) } nil end # Validate the syntax of a ruby file. Uses (Ruby 1.9+ only) RubyVM to # compile the code without evaluating it or spawning a new process. def validate_ruby_file_inline(ruby_file) # Even when we're compiling the code w/ RubyVM, syntax errors just # print to $stderr. We want to capture this and handle the printing # ourselves, so we must temporarily swap $stderr to capture the output. old_stderr = $stderr tmp_stderr = $stderr = StringIO.new abs_path = File.expand_path(ruby_file) file_content = IO.read(abs_path) # We have to wrap this in a block so the user code evaluates in a # similar context as what Chef does normally. Otherwise RubyVM # will reject some common idioms, like using `return` to end evaluation # of a recipe. See also CHEF-5199 wrapped_content = "Object.new.instance_eval do\n#{file_content}\nend\n" RubyVM::InstructionSequence.new(wrapped_content, ruby_file, abs_path, 0) true rescue SyntaxError $stderr = old_stderr invalid_ruby_file(ruby_file, tmp_stderr.string) false ensure # be paranoid about setting stderr back to the old value. $stderr = old_stderr if defined?(old_stderr) && old_stderr end # Debugs ruby syntax errors by printing the path to the file and any # diagnostic info given in +error_message+ def invalid_ruby_file(ruby_file, error_message) file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] Chef::Log.fatal("Cookbook file #{file_relative_path} has a ruby syntax error:") error_message.each_line { |l| Chef::Log.fatal(l.chomp) } false end # Returns the full path to the running ruby. def ruby Gem.ruby end end end end chef-12.3.0/lib/chef/cookbook/file_vendor.rb0000644000004100000410000000464112520074675020651 0ustar www-datawww-data# # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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. # class Chef class Cookbook # == Chef::Cookbook::FileVendor # This class handles fetching of cookbook files based on specificity. class FileVendor @vendor_class = nil @initialization_options = nil # Configures FileVendor to use the RemoteFileVendor implementation. After # calling this, subsequent calls to create_from_manifest will return a # RemoteFileVendor object initialized with the given http_client def self.fetch_from_remote(http_client) @vendor_class = RemoteFileVendor @initialization_options = http_client end def self.fetch_from_disk(cookbook_paths) @vendor_class = FileSystemFileVendor @initialization_options = cookbook_paths end # Returns the implementation class that is currently configured, or `nil` # if one has not been configured yet. def self.vendor_class @vendor_class end def self.initialization_options @initialization_options end # Factory method that creates the appropriate kind of # Cookbook::FileVendor to serve the contents of the manifest def self.create_from_manifest(manifest) if @vendor_class.nil? raise "Must configure FileVendor to use a specific implementation before creating an instance" end @vendor_class.new(manifest, @initialization_options) end # Gets the on-disk location for the given cookbook file. # # Subclasses are responsible for determining exactly how the # files are obtained and where they are stored. def get_filename(filename) raise NotImplemented, "Subclasses must implement this method" end end end end chef-12.3.0/lib/chef/cookbook/remote_file_vendor.rb0000644000004100000410000000650212520074675022222 0ustar www-datawww-data# # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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/cookbook/file_vendor' class Chef class Cookbook # == Chef::Cookbook::RemoteFileVendor # This FileVendor loads files by either fetching them from the local cache, or # if not available, loading them from the remote server. class RemoteFileVendor < FileVendor attr_reader :rest attr_reader :cookbook_name def initialize(manifest, rest) @manifest = manifest @cookbook_name = @manifest[:cookbook_name] || @manifest[:name] @rest = rest end # Implements abstract base's requirement. It looks in the # Chef::Config.cookbook_path file hierarchy for the requested # file. def get_filename(filename) if filename =~ /([^\/]+)\/(.+)$/ segment = $1 else raise "get_filename: Cannot determine segment/filename for incoming filename #{filename}" end raise "No such segment #{segment} in cookbook #{@cookbook_name}" unless @manifest[segment] found_manifest_record = @manifest[segment].find {|manifest_record| manifest_record[:path] == filename } raise "No such file #{filename} in #{@cookbook_name}" unless found_manifest_record cache_filename = File.join("cookbooks", @cookbook_name, found_manifest_record['path']) # update valid_cache_entries so the upstream cache cleaner knows what # we've used. validate_cached_copy(cache_filename) current_checksum = nil if Chef::FileCache.has_key?(cache_filename) current_checksum = Chef::CookbookVersion.checksum_cookbook_file(Chef::FileCache.load(cache_filename, false)) end # If the checksums are different between on-disk (current) and on-server # (remote, per manifest), do the update. This will also execute if there # is no current checksum. if current_checksum != found_manifest_record['checksum'] raw_file = @rest.get_rest(found_manifest_record[:url], true) Chef::Log.debug("Storing updated #{cache_filename} in the cache.") Chef::FileCache.move_to(raw_file.path, cache_filename) else Chef::Log.debug("Not fetching #{cache_filename}, as the cache is up to date.") Chef::Log.debug("current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})") end full_path_cache_filename = Chef::FileCache.load(cache_filename, false) # return the filename, not the contents (second argument= false) full_path_cache_filename end def validate_cached_copy(cache_filename) CookbookCacheCleaner.instance.mark_file_as_valid(cache_filename) end end end end chef-12.3.0/lib/chef/cookbook/cookbook_collection.rb0000644000004100000410000000325612520074675022377 0ustar www-datawww-data#-- # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2010 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/mash' class Chef # == Chef::CookbookCollection # This class is the consistent interface for a node to obtain its # cookbooks by name. # # This class is basically a glorified Hash, but since there are # several ways this cookbook information is collected, # (e.g. CookbookLoader for solo, hash of auto-vivified Cookbook # objects for lazily-loaded remote cookbooks), it gets transformed # into this. class CookbookCollection < Mash # The input is a mapping of cookbook name to CookbookVersion objects. We # simply extract them def initialize(cookbook_versions={}) super() do |hash, key| raise Chef::Exceptions::CookbookNotFound, "Cookbook #{key} not found. " << "If you're loading #{key} from another cookbook, make sure you configure the dependency in your metadata" end cookbook_versions.each{ |cookbook_name, cookbook_version| self[cookbook_name] = cookbook_version } end end end chef-12.3.0/lib/chef/cookbook/cookbook_version_loader.rb0000644000004100000410000002746712520074675023271 0ustar www-datawww-data require 'chef/cookbook_version' require 'chef/cookbook/chefignore' require 'chef/cookbook/metadata' require 'chef/util/path_helper' class Chef class Cookbook class CookbookVersionLoader FILETYPES_SUBJECT_TO_IGNORE = [ :attribute_filenames, :definition_filenames, :recipe_filenames, :template_filenames, :file_filenames, :library_filenames, :resource_filenames, :provider_filenames] UPLOADED_COOKBOOK_VERSION_FILE = ".uploaded-cookbook-version.json".freeze attr_reader :cookbook_settings attr_reader :cookbook_paths attr_reader :metadata_filenames attr_reader :frozen attr_reader :uploaded_cookbook_version_file attr_reader :cookbook_path # The cookbook's name as inferred from its directory. attr_reader :inferred_cookbook_name attr_reader :metadata_error def initialize(path, chefignore=nil) @cookbook_path = File.expand_path( path ) # cookbook_path from which this was loaded # We keep a list of all cookbook paths that have been merged in @cookbook_paths = [ cookbook_path ] @inferred_cookbook_name = File.basename( path ) @chefignore = chefignore @metadata = nil @relative_path = /#{Regexp.escape(@cookbook_path)}\/(.+)$/ @metadata_loaded = false @cookbook_settings = { :attribute_filenames => {}, :definition_filenames => {}, :recipe_filenames => {}, :template_filenames => {}, :file_filenames => {}, :library_filenames => {}, :resource_filenames => {}, :provider_filenames => {}, :root_filenames => {} } @metadata_filenames = [] @metadata_error = nil end # Load the cookbook. Raises an error if the cookbook_path given to the # constructor doesn't point to a valid cookbook. def load! file_paths_map = load if empty? raise Exceptions::CookbookNotFoundInRepo, "The directory #{cookbook_path} does not contain a cookbook" end file_paths_map end # Load the cookbook. Does not raise an error if given a non-cookbook # directory as the cookbook_path. This behavior is provided for # compatibility, it is recommended to use #load! instead. def load metadata # force lazy evaluation to occur # re-raise any exception that occurred when reading the metadata raise_metadata_error! load_as(:attribute_filenames, 'attributes', '*.rb') load_as(:definition_filenames, 'definitions', '*.rb') load_as(:recipe_filenames, 'recipes', '*.rb') load_recursively_as(:library_filenames, 'libraries', '*.rb') load_recursively_as(:template_filenames, "templates", "*") load_recursively_as(:file_filenames, "files", "*") load_recursively_as(:resource_filenames, "resources", "*.rb") load_recursively_as(:provider_filenames, "providers", "*.rb") load_root_files remove_ignored_files if empty? Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping." end @cookbook_settings end alias :load_cookbooks :load def metadata_filenames return @metadata_filenames unless @metadata_filenames.empty? if File.exists?(File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE)) @uploaded_cookbook_version_file = File.join(cookbook_path, UPLOADED_COOKBOOK_VERSION_FILE) end if File.exists?(File.join(cookbook_path, "metadata.rb")) @metadata_filenames << File.join(cookbook_path, "metadata.rb") elsif File.exists?(File.join(cookbook_path, "metadata.json")) @metadata_filenames << File.join(cookbook_path, "metadata.json") elsif @uploaded_cookbook_version_file @metadata_filenames << @uploaded_cookbook_version_file end # Set frozen based on .uploaded-cookbook-version.json set_frozen @metadata_filenames end def cookbook_version return nil if empty? Chef::CookbookVersion.new(cookbook_name, *cookbook_paths).tap do |c| c.attribute_filenames = cookbook_settings[:attribute_filenames].values c.definition_filenames = cookbook_settings[:definition_filenames].values c.recipe_filenames = cookbook_settings[:recipe_filenames].values c.template_filenames = cookbook_settings[:template_filenames].values c.file_filenames = cookbook_settings[:file_filenames].values c.library_filenames = cookbook_settings[:library_filenames].values c.resource_filenames = cookbook_settings[:resource_filenames].values c.provider_filenames = cookbook_settings[:provider_filenames].values c.root_filenames = cookbook_settings[:root_filenames].values c.metadata_filenames = metadata_filenames c.metadata = metadata c.freeze_version if @frozen end end def cookbook_name # The `name` attribute is now required in metadata, so # inferred_cookbook_name generally should not be used. Per CHEF-2923, # we have to not raise errors in cookbook metadata immediately, so that # users can still `knife cookbook upload some-cookbook` when an # unrelated cookbook has an error in its metadata. This situation # could prevent us from reading the `name` attribute from the metadata # entirely, but the name is used as a hash key in CookbookLoader, so we # fall back to the inferred name here. (metadata.name || @inferred_cookbook_name).to_sym end # Generates the Cookbook::Metadata object def metadata return @metadata unless @metadata.nil? @metadata = Chef::Cookbook::Metadata.new metadata_filenames.each do |metadata_file| case metadata_file when /\.rb$/ apply_ruby_metadata(metadata_file) when @uploaded_cookbook_version_file apply_json_cookbook_version_metadata(metadata_file) when /\.json$/ apply_json_metadata(metadata_file) else raise RuntimeError, "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}" end end @metadata # Rescue errors so that users can upload cookbooks via `knife cookbook # upload` even if some cookbooks in their chef-repo have errors in # their metadata. We only rescue StandardError because you have to be # doing something *really* terrible to raise an exception that inherits # directly from Exception in your metadata.rb file. rescue StandardError => e @metadata_error = e @metadata end def raise_metadata_error! raise @metadata_error unless @metadata_error.nil? # Metadata won't be valid if the cookbook is empty. If the cookbook is # actually empty, a metadata error here would be misleading, so don't # raise it (if called by #load!, a different error is raised). if !empty? && !metadata.valid? message = "Cookbook loaded at path(s) [#{@cookbook_paths.join(', ')}] has invalid metadata: #{metadata.errors.join('; ')}" raise Exceptions::MetadataNotValid, message end false end def empty? cookbook_settings.values.all? { |files_hash| files_hash.empty? } && metadata_filenames.size == 0 end def merge!(other_cookbook_loader) other_cookbook_settings = other_cookbook_loader.cookbook_settings cookbook_settings.each do |file_type, file_list| file_list.merge!(other_cookbook_settings[file_type]) end metadata_filenames.concat(other_cookbook_loader.metadata_filenames) @cookbook_paths += other_cookbook_loader.cookbook_paths @frozen = true if other_cookbook_loader.frozen @metadata = nil # reset metadata so it gets reloaded and all metadata files applied. self end def chefignore @chefignore ||= Chefignore.new(File.basename(cookbook_path)) end def load_root_files Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), '*'), File::FNM_DOTMATCH).each do |file| file = Chef::Util::PathHelper.cleanpath(file) next if File.directory?(file) next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file) cookbook_settings[:root_filenames][name] = file end end def load_recursively_as(category, category_dir, glob) file_spec = File.join(Chef::Util::PathHelper.escape_glob(cookbook_path, category_dir), '**', glob) Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file| file = Chef::Util::PathHelper.cleanpath(file) next if File.directory?(file) name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file) cookbook_settings[category][name] = file end end def load_as(category, *path_glob) Dir[File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), *path_glob)].each do |file| file = Chef::Util::PathHelper.cleanpath(file) name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file) cookbook_settings[category][name] = file end end def remove_ignored_files cookbook_settings.each_value do |file_list| file_list.reject! do |relative_path, full_path| chefignore.ignored?(relative_path) end end end def apply_ruby_metadata(file) begin @metadata.from_file(file) rescue Chef::Exceptions::JSON::ParseError Chef::Log.error("Error evaluating metadata.rb for #@inferred_cookbook_name in " + file) raise end end def apply_json_metadata(file) begin @metadata.from_json(IO.read(file)) rescue Chef::Exceptions::JSON::ParseError Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in " + file) raise end end def apply_json_cookbook_version_metadata(file) begin data = Chef::JSONCompat.parse(IO.read(file)) @metadata.from_hash(data['metadata']) # the JSON cookbok metadata file is only used by chef-zero. # The Chef Server API currently does not enforce that the metadata # have a `name` field, but that will cause an error when attempting # to load the cookbook. To keep compatibility, we fake it by setting # the metadata name from the cookbook version object's name. # # This behavior can be removed if/when Chef Server enforces that the # metadata contains a name key. @metadata.name(data['cookbook_name']) unless data['metadata'].key?('name') rescue Chef::Exceptions::JSON::ParseError Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in " + file) raise end end def set_frozen if uploaded_cookbook_version_file begin data = Chef::JSONCompat.parse(IO.read(uploaded_cookbook_version_file)) @frozen = data['frozen?'] rescue Chef::Exceptions::JSON::ParseError Chef::Log.error("Couldn't parse cookbook metadata JSON for #@inferred_cookbook_name in #{uploaded_cookbook_version_file}") raise end end end end end end chef-12.3.0/lib/chef/search/0000755000004100000410000000000012520074675015462 5ustar www-datawww-datachef-12.3.0/lib/chef/search/query.rb0000644000004100000410000001157712520074675017167 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/config' require 'chef/exceptions' require 'chef/rest' require 'uri' class Chef class Search class Query attr_accessor :rest attr_reader :config def initialize(url=nil, config:Chef::Config) @config = config @url = url end def rest @rest ||= Chef::REST.new(@url || @config[:chef_server_url]) end # Backwards compatability for cookbooks. # This can be removed in Chef > 12. def partial_search(type, query='*:*', *args, &block) Chef::Log.warn(<<-WARNDEP) DEPRECATED: The 'partial_search' API is deprecated and will be removed in future releases. Please use 'search' with a :filter_result argument to get partial search data. WARNDEP if !args.empty? && args.first.is_a?(Hash) # partial_search uses :keys instead of :filter_result for # result filtering. args_h = args.first.dup args_h[:filter_result] = args_h[:keys] args_h.delete(:keys) search(type, query, args_h, &block) else search(type, query, *args, &block) end end # # New search input, designed to be backwards compatible with the old method signature # 'type' and 'query' are the same as before, args now will accept either a Hash of # search arguments with symbols as the keys (ie :sort, :start, :rows) and a :filter_result # option. # # :filter_result should be in the format of another Hash with the structure of: # { # :returned_name1 => ["path", "to", "variable"], # :returned_name2 => ["shorter", "path"] # } # a real world example might be something like: # { # :ip_address => ["ipaddress"], # :ruby_version => ["languages", "ruby", "version"] # } # this will bring back 2 variables 'ip_address' and 'ruby_version' with whatever value was found # an example of the returned json may be: # {"ip_address":"127.0.0.1", "ruby_version": "1.9.3"} # def search(type, query='*:*', *args, &block) validate_type(type) args_h = hashify_args(*args) response = call_rest_service(type, query: query, **args_h) if block response["rows"].each { |row| block.call(row) if row } unless (response["start"] + response["rows"].length) >= response["total"] args_h[:start] = response["start"] + response["rows"].length search(type, query, args_h, &block) end true else [ response["rows"], response["start"], response["total"] ] end end private def validate_type(t) unless t.kind_of?(String) || t.kind_of?(Symbol) msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." + "Usage: search(:node, QUERY[, OPTIONAL_ARGS])" + " `knife search environment QUERY (options)`" raise Chef::Exceptions::InvalidSearchQuery, msg end end def hashify_args(*args) return Hash.new if args.empty? return args.first if args.first.is_a?(Hash) args_h = Hash.new args_h[:sort] = args[0] if args[0] args_h[:start] = args[1] if args[1] args_h[:rows] = args[2] args_h[:filter_result] = args[3] args_h end def escape(s) s && URI.escape(s.to_s) end def create_query_string(type, query, rows, start, sort) qstr = "search/#{type}?q=#{escape(query)}" qstr += "&sort=#{escape(sort)}" if sort qstr += "&start=#{escape(start)}" if start qstr += "&rows=#{escape(rows)}" if rows qstr end def call_rest_service(type, query:'*:*', rows:nil, start:0, sort:'X_CHEF_id_CHEF_X asc', filter_result:nil) query_string = create_query_string(type, query, rows, start, sort) if filter_result response = rest.post_rest(query_string, filter_result) # response returns rows in the format of # { "url" => url_to_node, "data" => filter_result_hash } response['rows'].map! { |row| row['data'] } else response = rest.get_rest(query_string) end response end end end end chef-12.3.0/lib/chef/org.rb0000644000004100000410000000762412520074675015342 0ustar www-datawww-data# # Author:: Steven Danna (steve@opscode.com) # Copyright:: Copyright (c) 2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/json_compat' require 'chef/mixin/params_validate' require 'chef/rest' class Chef class Org include Chef::Mixin::ParamsValidate def initialize(name) @name = name @full_name = '' # The Chef API returns the private key of the validator # client on create @private_key = nil @guid = nil end def chef_rest @chef_rest ||= Chef::REST.new(Chef::Config[:chef_server_root]) end def name(arg=nil) set_or_return(:name, arg, :regex => /^[a-z0-9\-_]+$/) end def full_name(arg=nil) set_or_return(:full_name, arg, :kind_of => String) end def private_key(arg=nil) set_or_return(:private_key, arg, :kind_of => String) end def guid(arg=nil) set_or_return(:guid, arg, :kind_of => String) end def to_hash result = { "name" => @name, "full_name" => @full_name } result["private_key"] = @private_key if @private_key result["guid"] = @guid if @guid result end def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def create payload = {:name => self.name, :full_name => self.full_name} new_org = chef_rest.post_rest("organizations", payload) Chef::Org.from_hash(self.to_hash.merge(new_org)) end def update payload = {:name => self.name, :full_name => self.full_name} new_org = chef_rest.put_rest("organizations/#{name}", payload) Chef::Org.from_hash(self.to_hash.merge(new_org)) end def destroy chef_rest.delete_rest("organizations/#{@name}") end def save begin create rescue Net::HTTPServerException => e if e.response.code == "409" update else raise e end end end def associate_user(username) request_body = {:user => username} response = chef_rest.post_rest "organizations/#{@name}/association_requests", request_body association_id = response["uri"].split("/").last chef_rest.put_rest "users/#{username}/association_requests/#{association_id}", { :response => 'accept' } end def dissociate_user(username) chef_rest.delete_rest "organizations/#{name}/users/#{username}" end # Class methods def self.from_hash(org_hash) org = Chef::Org.new(org_hash['name']) org.full_name org_hash['full_name'] org.private_key org_hash['private_key'] if org_hash.key?('private_key') org.guid org_hash['guid'] if org_hash.key?('guid') org end def self.from_json(json) Chef::Org.from_hash(Chef::JSONCompat.from_json(json)) end class <) # 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. # # # PURPOSE: This strategy preserves the inode, and will preserve modes + ownership # even if the user running chef cannot create that ownership (but has # rights to the file). It is vulnerable to crashes in the middle of # writing the file which could result in corruption or zero-length files. # class Chef class FileContentManagement class Deploy # # PURPOSE: This strategy preserves the inode, and will preserve modes + ownership # even if the user running chef cannot create that ownership (but has # rights to the file). It is vulnerable to crashes in the middle of # writing the file which could result in corruption or zero-length files. # class Cp def create(file) Chef::Log.debug("touching #{file} to create it") FileUtils.touch(file) end def deploy(src, dst) Chef::Log.debug("copying temporary file #{src} into place at #{dst}") FileUtils.cp(src, dst) end end end end end chef-12.3.0/lib/chef/file_content_management/deploy/mv_windows.rb0000644000004100000410000000570512520074675025106 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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. # # # We update the contents of the file, using mv for atomicity, while maintaining all the # ACL information on the dst file. # require 'chef/platform/query_helpers' if Chef::Platform.windows? require 'chef/win32/security' end class Chef class FileContentManagement class Deploy class MvWindows Security = Chef::ReservedNames::Win32::Security ACL = Security::ACL def create(file) Chef::Log.debug("touching #{file} to create it") FileUtils.touch(file) end def deploy(src, dst) # # At the time of deploy ACLs are correctly configured on the # dst. This would be a simple atomic move operations in # windows was not converting inherited ACLs of src to # non-inherited ACLs in certain cases.See: # http://blogs.msdn.com/b/oldnewthing/archive/2006/08/24/717181.aspx # # # First cache the ACLs of dst file # dst_so = Security::SecurableObject.new(dst) begin # get the sd with the SACL dst_sd = dst_so.security_descriptor(true) rescue Chef::Exceptions::Win32APIError # Catch and raise if the user is not elevated enough. # At this point we can't configure the file as expected so # we're failing action on the resource. raise Chef::Exceptions::WindowsNotAdmin, "can not get the security information for '#{dst}' due to missing Administrator privileges." end if dst_sd.dacl_present? apply_dacl = ACL.create(dst_sd.dacl.select { |ace| !ace.inherited? }) end if dst_sd.sacl_present? apply_sacl = ACL.create(dst_sd.sacl.select { |ace| !ace.inherited? }) end # # Then deploy the file # FileUtils.mv(src, dst) # # Then apply the cached acls to the new dst file # dst_so = Security::SecurableObject.new(dst) dst_so.group = dst_sd.group dst_so.owner = dst_sd.owner dst_so.set_dacl(apply_dacl, dst_sd.dacl_inherits?) if dst_sd.dacl_present? dst_so.set_sacl(apply_sacl, dst_sd.sacl_inherits?) if dst_sd.sacl_present? end end end end end chef-12.3.0/lib/chef/file_content_management/deploy/mv_unix.rb0000644000004100000410000000620112520074675024367 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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. # class Chef class FileContentManagement class Deploy # # PURPOSE: this strategy is atomic, and attempts to preserve file modes # # NOTE: there is no preserve flag to FileUtils.mv, and we want to preserve the dst file # modes rather than the src file modes (preserve = true is what mv does already, we # would like preserve = false which is tricky). # class MvUnix def create(file) # this is very simple, but it ensures that ownership and file modes take # good defaults, in particular mode needs to obey umask on create Chef::Log.debug("touching #{file} to create it") FileUtils.touch(file) end def deploy(src, dst) # we are only responsible for content so restore the dst files perms Chef::Log.debug("reading modes from #{dst} file") stat = ::File.stat(dst) mode = stat.mode & 07777 uid = stat.uid gid = stat.gid Chef::Log.debug("applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}") # i own the inode, so should be able to at least chmod it ::File.chmod(mode, src) # we may be running as non-root in which case because we are doing an mv we cannot preserve # the file modes. after the mv we have a different inode and if we don't have rights to # chown/chgrp on the inode then we can't fix the ownership. # # in the case where i'm running chef-solo on my homedir as myself and some root-shell # work has caused dotfiles of mine to change to root-owned, i'm fine with this not being # exceptional, and i think most use cases will consider this to not be exceptional, and # the right thing is to fix the ownership of the file to the user running the commmand # (which requires write perms to the directory, or mv will throw an exception) begin ::File.chown(uid, nil, src) rescue Errno::EPERM Chef::Log.warn("Could not set uid = #{uid} on #{src}, file modes not preserved") end begin ::File.chown(nil, gid, src) rescue Errno::EPERM Chef::Log.warn("Could not set gid = #{gid} on #{src}, file modes not preserved") end Chef::Log.debug("moving temporary file #{src} into place at #{dst}") FileUtils.mv(src, dst) end end end end end chef-12.3.0/lib/chef/file_content_management/tempfile.rb0000644000004100000410000000620312520074675023215 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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 "tempfile" class Chef class FileContentManagement class Tempfile attr_reader :new_resource def initialize(new_resource) @new_resource = new_resource end def tempfile @tempfile ||= tempfile_open end private def tempfile_open tf = nil errors = [ ] tempfile_dirnames.each do |tempfile_dirname| begin tf = ::Tempfile.open(tempfile_basename, tempfile_dirname) break rescue SystemCallError => e message = "Creating temp file under '#{tempfile_dirname}' failed with: '#{e.message}'" Chef::Log.debug(message) errors << message end end raise Chef::Exceptions::FileContentStagingError(errors) if tf.nil? # We always process the tempfile in binmode so that we # preserve the line endings of the content. tf.binmode tf end # # These are important for windows to get permissions right, and may # be useful for SELinux and other ACL approaches. Please use them # as the arguments to Tempfile.new() consistently. # def tempfile_basename basename = ::File.basename(@new_resource.name) basename.insert 0, "." unless Chef::Platform.windows? # dotfile if we're not on windows basename end # Returns the possible directories for the tempfile to be created in. def tempfile_dirnames # in why-run mode we need to create a Tempfile to compare against, which we will never # wind up deploying, but our enclosing directory for the destdir may not exist yet, so # instead we can reliably always create a Tempfile to compare against in Dir::tmpdir if Chef::Config[:why_run] [ Dir.tmpdir ] else case Chef::Config[:file_staging_uses_destdir] when :auto # In auto mode we try the destination directory first and fallback to ENV['TMP'] if # that doesn't work. [ ::File.dirname(@new_resource.path), Dir.tmpdir ] when true [ ::File.dirname(@new_resource.path) ] when false [ Dir.tmpdir ] else raise Chef::Exceptions::ConfigurationError, "Unknown setting '#{Chef::Config[:file_staging_uses_destdir]}' for Chef::Config[:file_staging_uses_destdir]. Possible values are :auto, true or false." end end end end end end chef-12.3.0/lib/chef/file_content_management/deploy.rb0000644000004100000410000000217112520074675022704 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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/file_content_management/deploy/cp' require 'chef/file_content_management/deploy/mv_unix' if Chef::Platform.windows? require 'chef/file_content_management/deploy/mv_windows' end class Chef class FileContentManagement class Deploy def self.strategy(atomic_update) if atomic_update Chef::Platform.windows? ? MvWindows.new() : MvUnix.new() else Cp.new() end end end end end chef-12.3.0/lib/chef/file_content_management/content_base.rb0000644000004100000410000000314612520074675024057 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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. # class Chef class FileContentManagement class ContentBase attr_reader :run_context attr_reader :new_resource attr_reader :current_resource def initialize(new_resource, current_resource, run_context) @new_resource = new_resource @current_resource = current_resource @run_context = run_context @tempfile_loaded = false end def tempfile # tempfile may be nil, so we cannot use ||= here if @tempfile_loaded @tempfile else @tempfile_loaded = true @tempfile = file_for_provider end end private # # Return something that looks like a File or Tempfile and # you must assume the provider will unlink this file. Copy # the contents to a Tempfile if you need to. # def file_for_provider raise "class must implement file_for_provider!" end end end end chef-12.3.0/lib/chef/digester.rb0000644000004100000410000000336312520074675016355 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 'openssl' require 'singleton' class Chef class Digester include Singleton def self.checksum_for_file(*args) instance.checksum_for_file(*args) end def validate_checksum(*args) self.class.validate_checksum(*args) end def checksum_for_file(file) generate_checksum(file) end def generate_checksum(file) checksum_file(file, OpenSSL::Digest::SHA256.new) end def self.generate_md5_checksum_for_file(*args) instance.generate_md5_checksum_for_file(*args) end def generate_md5_checksum_for_file(file) checksum_file(file, OpenSSL::Digest::MD5.new) end def generate_md5_checksum(io) checksum_io(io, OpenSSL::Digest::MD5.new) end private def checksum_file(file, digest) File.open(file, 'rb') { |f| checksum_io(f, digest) } end def checksum_io(io, digest) while chunk = io.read(1024 * 8) digest.update(chunk) end digest.hexdigest end end end chef-12.3.0/lib/chef/provider.rb0000644000004100000410000001316712520074675016404 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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/mixin/from_file' require 'chef/mixin/convert_to_class_name' require 'chef/mixin/enforce_ownership_and_permissions' require 'chef/mixin/why_run' require 'chef/mixin/shell_out' require 'chef/mixin/provides' require 'chef/platform/service_helpers' require 'chef/node_map' class Chef class Provider include Chef::Mixin::WhyRun include Chef::Mixin::ShellOut extend Chef::Mixin::Provides # supports the given resource and action (late binding) def self.supports?(resource, action) true end attr_accessor :new_resource attr_accessor :current_resource attr_accessor :run_context attr_reader :recipe_name attr_reader :cookbook_name #-- # TODO: this should be a reader, and the action should be passed in the # constructor; however, many/most subclasses override the constructor so # changing the arity would be a breaking change. Change this at the next # break, e.g., Chef 11. attr_accessor :action def initialize(new_resource, run_context) @new_resource = new_resource @action = action @current_resource = nil @run_context = run_context @converge_actions = nil @recipe_name = nil @cookbook_name = nil end def whyrun_mode? Chef::Config[:why_run] end def whyrun_supported? false end def node run_context && run_context.node end # Used by providers supporting embedded recipes def resource_collection run_context && run_context.resource_collection end def cookbook_name new_resource.cookbook_name end def load_current_resource raise Chef::Exceptions::Override, "You must override load_current_resource in #{self.to_s}" end def define_resource_requirements end def cleanup_after_converge end def action_nothing Chef::Log.debug("Doing nothing for #{@new_resource.to_s}") true end def events run_context.events end def run_action(action=nil) @action = action unless action.nil? # TODO: it would be preferable to get the action to be executed in the # constructor... # user-defined LWRPs may include unsafe load_current_resource methods that cannot be run in whyrun mode if !whyrun_mode? || whyrun_supported? load_current_resource events.resource_current_state_loaded(@new_resource, @action, @current_resource) elsif whyrun_mode? && !whyrun_supported? events.resource_current_state_load_bypassed(@new_resource, @action, @current_resource) end define_resource_requirements process_resource_requirements # user-defined providers including LWRPs may # not include whyrun support - if they don't support it # we can't execute any actions while we're running in # whyrun mode. Instead we 'fake' whyrun by documenting that # we can't execute the action. # in non-whyrun mode, this will still cause the action to be # executed normally. if whyrun_supported? && !requirements.action_blocked?(@action) send("action_#{@action}") elsif whyrun_mode? events.resource_bypassed(@new_resource, @action, self) else send("action_#{@action}") end set_updated_status cleanup_after_converge end def process_resource_requirements requirements.run(:all_actions) unless @action == :nothing requirements.run(@action) end def resource_updated? !converge_actions.empty? || @new_resource.updated_by_last_action? end def set_updated_status if !resource_updated? events.resource_up_to_date(@new_resource, @action) else events.resource_updated(@new_resource, @action) new_resource.updated_by_last_action(true) end end def requirements @requirements ||= ResourceRequirements.new(@new_resource, run_context) end def converge_by(descriptions, &block) converge_actions.add_action(descriptions, &block) end protected def converge_actions @converge_actions ||= ConvergeActions.new(@new_resource, run_context, @action) end def recipe_eval(&block) # This block has new resource definitions within it, which # essentially makes it an in-line Chef run. Save our current # run_context and create one anew, so the new Chef run only # executes the embedded resources. # # TODO: timh,cw: 2010-5-14: This means that the resources within # this block cannot interact with resources outside, e.g., # manipulating notifies. converge_by ("evaluate block and run any associated actions") do saved_run_context = @run_context @run_context = @run_context.dup @run_context.resource_collection = Chef::ResourceCollection.new instance_eval(&block) Chef::Runner.new(@run_context).converge @run_context = saved_run_context end end end end chef-12.3.0/lib/chef/client.rb0000644000004100000410000004415612520074675016032 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Christopher Brown () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2008-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 'chef/config' require 'chef/mixin/params_validate' require 'chef/mixin/path_sanity' require 'chef/log' require 'chef/rest' require 'chef/api_client' require 'chef/api_client/registration' require 'chef/audit/runner' require 'chef/node' require 'chef/role' require 'chef/file_cache' require 'chef/run_context' require 'chef/runner' require 'chef/run_status' require 'chef/cookbook/cookbook_collection' require 'chef/cookbook/file_vendor' require 'chef/cookbook/file_system_file_vendor' require 'chef/cookbook/remote_file_vendor' require 'chef/event_dispatch/dispatcher' require 'chef/event_loggers/base' require 'chef/event_loggers/windows_eventlog' require 'chef/exceptions' require 'chef/formatters/base' require 'chef/formatters/doc' require 'chef/formatters/minimal' require 'chef/version' require 'chef/resource_reporter' require 'chef/audit/audit_reporter' require 'chef/run_lock' require 'chef/policy_builder' require 'chef/request_id' require 'chef/platform/rebooter' require 'ohai' require 'rbconfig' class Chef # == Chef::Client # The main object in a Chef run. Preps a Chef::Node and Chef::RunContext, # syncs cookbooks if necessary, and triggers convergence. class Client include Chef::Mixin::PathSanity # IO stream that will be used as 'STDOUT' for formatters. Formatters are # configured during `initialize`, so this provides a convenience for # setting alternative IO stream during tests. STDOUT_FD = STDOUT # IO stream that will be used as 'STDERR' for formatters. Formatters are # configured during `initialize`, so this provides a convenience for # setting alternative IO stream during tests. STDERR_FD = STDERR # Clears all notifications for client run status events. # Primarily for testing purposes. def self.clear_notifications @run_start_notifications = nil @run_completed_successfully_notifications = nil @run_failed_notifications = nil end # The list of notifications to be run when the client run starts. def self.run_start_notifications @run_start_notifications ||= [] end # The list of notifications to be run when the client run completes # successfully. def self.run_completed_successfully_notifications @run_completed_successfully_notifications ||= [] end # The list of notifications to be run when the client run fails. def self.run_failed_notifications @run_failed_notifications ||= [] end # Add a notification for the 'client run started' event. The notification # is provided as a block. The current Chef::RunStatus object will be passed # to the notification_block when the event is triggered. def self.when_run_starts(¬ification_block) run_start_notifications << notification_block end # Add a notification for the 'client run success' event. The notification # is provided as a block. The current Chef::RunStatus object will be passed # to the notification_block when the event is triggered. def self.when_run_completes_successfully(¬ification_block) run_completed_successfully_notifications << notification_block end # Add a notification for the 'client run failed' event. The notification # is provided as a block. The current Chef::RunStatus is passed to the # notification_block when the event is triggered. def self.when_run_fails(¬ification_block) run_failed_notifications << notification_block end # Callback to fire notifications that the Chef run is starting def run_started self.class.run_start_notifications.each do |notification| notification.call(run_status) end @events.run_started(run_status) end # Callback to fire notifications that the run completed successfully def run_completed_successfully success_handlers = self.class.run_completed_successfully_notifications success_handlers.each do |notification| notification.call(run_status) end end # Callback to fire notifications that the Chef run failed def run_failed failure_handlers = self.class.run_failed_notifications failure_handlers.each do |notification| notification.call(run_status) end end attr_accessor :node attr_accessor :ohai attr_accessor :rest attr_accessor :runner attr_reader :json_attribs attr_reader :run_status attr_reader :events # Creates a new Chef::Client. def initialize(json_attribs=nil, args={}) @json_attribs = json_attribs || {} @node = nil @run_status = nil @runner = nil @ohai = Ohai::System.new event_handlers = configure_formatters + configure_event_loggers event_handlers += Array(Chef::Config[:event_handlers]) @events = EventDispatch::Dispatcher.new(*event_handlers) @override_runlist = args.delete(:override_runlist) @specific_recipes = args.delete(:specific_recipes) if new_runlist = args.delete(:runlist) @json_attribs["run_list"] = new_runlist end # these slurp in the resource+provider world, so be exceedingly lazy about requiring them require 'chef/platform/provider_priority_map' unless defined? Chef::Platform::ProviderPriorityMap require 'chef/platform/resource_priority_map' unless defined? Chef::Platform::ResourcePriorityMap Chef.set_provider_priority_map(Chef::Platform::ProviderPriorityMap.instance) Chef.set_resource_priority_map(Chef::Platform::ResourcePriorityMap.instance) end def configure_formatters formatters_for_run.map do |formatter_name, output_path| if output_path.nil? Chef::Formatters.new(formatter_name, STDOUT_FD, STDERR_FD) else io = File.open(output_path, "a+") io.sync = true Chef::Formatters.new(formatter_name, io, io) end end end def formatters_for_run if Chef::Config.formatters.empty? [default_formatter] else Chef::Config.formatters end end def default_formatter if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter] [:doc] else [:null] end end def configure_event_loggers if Chef::Config.disable_event_logger [] else Chef::Config.event_loggers.map do |evt_logger| case evt_logger when Symbol Chef::EventLoggers.new(evt_logger) when Class evt_logger.new else end end end end # Resource repoters send event information back to the chef server for processing. # Can only be called after we have a @rest object def register_reporters [ Chef::ResourceReporter.new(rest), Chef::Audit::AuditReporter.new(rest) ].each do |r| events.register(r) end end # Instantiates a Chef::Node object, possibly loading the node's prior state # when using chef-client. Delegates to policy_builder. Injects the built node # into the Chef class. # # @return [Chef::Node] The node object for this Chef run def load_node policy_builder.load_node @node = policy_builder.node Chef.set_node(@node) node end # Mutates the `node` object to prepare it for the chef run. Delegates to # policy_builder # # @return [Chef::Node] The updated node object def build_node policy_builder.build_node @run_status = Chef::RunStatus.new(node, events) node end def setup_run_context run_context = policy_builder.setup_run_context(@specific_recipes) assert_cookbook_path_not_empty(run_context) run_status.run_context = run_context run_context end def sync_cookbooks policy_builder.sync_cookbooks end def policy_builder @policy_builder ||= Chef::PolicyBuilder.strategy.new(node_name, ohai.data, json_attribs, @override_runlist, events) end def save_updated_node if Chef::Config[:solo] # nothing to do elsif policy_builder.temporary_policy? Chef::Log.warn("Skipping final node save because override_runlist was given") else Chef::Log.debug("Saving the current state of node #{node_name}") @node.save end end def run_ohai filter = Chef::Config[:minimal_ohai] ? %w[fqdn machinename hostname platform platform_version os os_version] : nil ohai.all_plugins(filter) @events.ohai_completed(node) end def node_name name = Chef::Config[:node_name] || ohai[:fqdn] || ohai[:machinename] || ohai[:hostname] Chef::Config[:node_name] = name raise Chef::Exceptions::CannotDetermineNodeName unless name # node names > 90 bytes only work with authentication protocol >= 1.1 # see discussion in config.rb. if name.bytesize > 90 Chef::Config[:authentication_protocol_version] = "1.1" end name end # # === Returns # rest:: returns Chef::REST connection object def register(client_name=node_name, config=Chef::Config) if !config[:client_key] @events.skipping_registration(client_name, config) Chef::Log.debug("Client key is unspecified - skipping registration") elsif File.exists?(config[:client_key]) @events.skipping_registration(client_name, config) Chef::Log.debug("Client key #{config[:client_key]} is present - skipping registration") else @events.registration_start(node_name, config) Chef::Log.info("Client key #{config[:client_key]} is not present - registering") Chef::ApiClient::Registration.new(node_name, config[:client_key]).run @events.registration_completed end # We now have the client key, and should use it from now on. @rest = Chef::REST.new(config[:chef_server_url], client_name, config[:client_key]) register_reporters rescue Exception => e # TODO: munge exception so a semantic failure message can be given to the # user @events.registration_failed(client_name, e, config) raise end # Converges the node. # # === Returns # The thrown exception, if there was one. If this returns nil the converge was successful. def converge(run_context) converge_exception = nil catch(:end_client_run_early) do begin @events.converge_start(run_context) Chef::Log.debug("Converging node #{node_name}") @runner = Chef::Runner.new(run_context) runner.converge @events.converge_complete rescue Exception => e @events.converge_failed(e) raise e if Chef::Config[:audit_mode] == :disabled converge_exception = e end end converge_exception end # We don't want to change the old API on the `converge` method to have it perform # saving. So we wrap it in this method. def converge_and_save(run_context) converge_exception = converge(run_context) unless converge_exception begin save_updated_node rescue Exception => e raise e if Chef::Config[:audit_mode] == :disabled converge_exception = e end end converge_exception end def run_audits(run_context) audit_exception = nil begin @events.audit_phase_start(run_status) Chef::Log.info("Starting audit phase") auditor = Chef::Audit::Runner.new(run_context) auditor.run if auditor.failed? raise Chef::Exceptions::AuditsFailed.new(auditor.num_failed, auditor.num_total) end @events.audit_phase_complete rescue Exception => e Chef::Log.error("Audit phase failed with error message: #{e.message}") @events.audit_phase_failed(e) audit_exception = e end audit_exception end # Expands the run list. Delegates to the policy_builder. # # Normally this does not need to be called from here, it will be called by # build_node. This is provided so external users (like the chefspec # project) can inject custom behavior into the run process. # # === Returns # RunListExpansion: A RunListExpansion or API compatible object. def expanded_run_list policy_builder.expand_run_list end def do_windows_admin_check if Chef::Platform.windows? Chef::Log.debug("Checking for administrator privileges....") if !has_admin_privileges? message = "chef-client doesn't have administrator privileges on node #{node_name}." if Chef::Config[:fatal_windows_admin_check] Chef::Log.fatal(message) Chef::Log.fatal("fatal_windows_admin_check is set to TRUE.") raise Chef::Exceptions::WindowsNotAdmin, message else Chef::Log.warn("#{message} This might cause unexpected resource failures.") end else Chef::Log.debug("chef-client has administrator privileges on node #{node_name}.") end end end # Do a full run for this Chef::Client. Calls: # # * run_ohai - Collect information about the system # * build_node - Get the last known state, merge with local changes # * register - If not in solo mode, make sure the server knows about this client # * sync_cookbooks - If not in solo mode, populate the local cache with the node's cookbooks # * converge - Bring this system up to date # # === Returns # true:: Always returns true. def run runlock = RunLock.new(Chef::Config.lockfile) runlock.acquire # don't add code that may fail before entering this section to be sure to release lock begin runlock.save_pid request_id = Chef::RequestID.instance.request_id run_context = nil @events.run_start(Chef::VERSION) Chef::Log.info("*** Chef #{Chef::VERSION} ***") Chef::Log.info "Chef-client pid: #{Process.pid}" Chef::Log.debug("Chef-client request_id: #{request_id}") enforce_path_sanity run_ohai register unless Chef::Config[:solo] load_node build_node run_status.run_id = request_id run_status.start_clock Chef::Log.info("Starting Chef Run for #{node.name}") run_started do_windows_admin_check run_context = setup_run_context if Chef::Config[:audit_mode] != :audit_only converge_error = converge_and_save(run_context) end if Chef::Config[:why_run] == true # why_run should probably be renamed to why_converge Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes") elsif Chef::Config[:audit_mode] != :disabled audit_error = run_audits(run_context) end if converge_error || audit_error e = Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error) e.fill_backtrace raise e end run_status.stop_clock Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds") run_completed_successfully @events.run_completed(node) # rebooting has to be the last thing we do, no exceptions. Chef::Platform::Rebooter.reboot_if_needed!(node) true rescue Exception => e # CHEF-3336: Send the error first in case something goes wrong below and we don't know why Chef::Log.debug("Re-raising exception: #{e.class} - #{e.message}\n#{e.backtrace.join("\n ")}") # If we failed really early, we may not have a run_status yet. Too early for these to be of much use. if run_status run_status.stop_clock run_status.exception = e run_failed end Chef::Application.debug_stacktrace(e) @events.run_failed(e) raise ensure Chef::RequestID.instance.reset_request_id request_id = nil @run_status = nil run_context = nil runlock.release GC.start end true end private def empty_directory?(path) !File.exists?(path) || (Dir.entries(path).size <= 2) end def is_last_element?(index, object) object.kind_of?(Array) ? index == object.size - 1 : true end def assert_cookbook_path_not_empty(run_context) if Chef::Config[:solo] # Check for cookbooks in the path given # Chef::Config[:cookbook_path] can be a string or an array # if it's an array, go through it and check each one, raise error at the last one if no files are found cookbook_paths = Array(Chef::Config[:cookbook_path]) Chef::Log.debug "Loading from cookbook_path: #{cookbook_paths.map { |path| File.expand_path(path) }.join(', ')}" if cookbook_paths.all? {|path| empty_directory?(path) } msg = "None of the cookbook paths set in Chef::Config[:cookbook_path], #{cookbook_paths.inspect}, contain any cookbooks" Chef::Log.fatal(msg) raise Chef::Exceptions::CookbookNotFound, msg end else Chef::Log.warn("Node #{node_name} has an empty run list.") if run_context.node.run_list.empty? end end def has_admin_privileges? require 'chef/win32/security' Chef::ReservedNames::Win32::Security.has_admin_privileges? end end end # HACK cannot load this first, but it must be loaded. require 'chef/cookbook_loader' require 'chef/cookbook_version' require 'chef/cookbook/synchronizer' chef-12.3.0/lib/chef/resource_reporter.rb0000644000004100000410000002604012520074675020315 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Prajakta Purohit (prajakta@opscode.com>) # Auther:: Tyler Cloke () # # 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 'uri' require 'securerandom' require 'chef/event_dispatch/base' class Chef class ResourceReporter < EventDispatch::Base class ResourceReport < Struct.new(:new_resource, :current_resource, :action, :exception, :elapsed_time) def self.new_with_current_state(new_resource, action, current_resource) report = new report.new_resource = new_resource report.action = action report.current_resource = current_resource report end def self.new_for_exception(new_resource, action) report = new report.new_resource = new_resource report.action = action report end # Future: Some resources store state information that does not convert nicely # to json. We can't call a resource's state method here, since there are conflicts # with some LWRPs, so we can't override a resource's state method to return # json-friendly state data. # # The registry key resource returns json-friendly state data through its state # attribute, and uses a read-only variable for fetching true state data. If # we have conflicts with other resources reporting json incompatible state, we # may want to extend the state_attrs API with the ability to rename POST'd # attrs. def for_json as_hash = {} as_hash["type"] = new_resource.class.dsl_name as_hash["name"] = new_resource.name.to_s as_hash["id"] = new_resource.identity.to_s as_hash["after"] = state(new_resource) as_hash["before"] = current_resource ? state(current_resource) : {} as_hash["duration"] = (elapsed_time * 1000).to_i.to_s as_hash["delta"] = new_resource.diff if new_resource.respond_to?("diff") as_hash["delta"] = "" if as_hash["delta"].nil? # TODO: rename as "action" as_hash["result"] = action.to_s if success? else #as_hash["result"] = "failed" end if new_resource.cookbook_name as_hash["cookbook_name"] = new_resource.cookbook_name as_hash["cookbook_version"] = new_resource.cookbook_version.version end as_hash end def finish self.elapsed_time = new_resource.elapsed_time end def success? !self.exception end def state(r) r.class.state_attrs.inject({}) do |state_attrs, attr_name| state_attrs[attr_name] = r.send(attr_name) state_attrs end end end # End class ResouceReport attr_reader :updated_resources attr_reader :status attr_reader :exception attr_reader :run_id attr_reader :error_descriptions PROTOCOL_VERSION = '0.1.0' def initialize(rest_client) if Chef::Config[:enable_reporting] && !Chef::Config[:why_run] @reporting_enabled = true else @reporting_enabled = false end @updated_resources = [] @total_res_count = 0 @pending_update = nil @status = "success" @exception = nil @rest_client = rest_client @error_descriptions = {} end def run_started(run_status) @run_status = run_status if reporting_enabled? begin resource_history_url = "reports/nodes/#{node_name}/runs" server_response = @rest_client.post_rest(resource_history_url, {:action => :start, :run_id => run_id, :start_time => start_time.to_s}, headers) rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e handle_error_starting_run(e, resource_history_url) end end end def handle_error_starting_run(e, url) message = "Reporting error starting run. URL: #{url} " code = if e.response.code e.response.code.to_s else "Exception Code Empty" end if !e.response || (code != "404" && code != "406") exception = "Exception: #{code} " if Chef::Config[:enable_reporting_url_fatals] reporting_status = "Reporting fatals enabled. Aborting run. " Chef::Log.error(message + exception + reporting_status) raise else reporting_status = "Disabling reporting for run." Chef::Log.info(message + exception + reporting_status) end else reason = "Received #{code}. " if code == "406" reporting_status = "Client version not supported. Please update the client. Disabling reporting for run." Chef::Log.info(message + reason + reporting_status) else reporting_status = "Disabling reporting for run." Chef::Log.debug(message + reason + reporting_status) end end @reporting_enabled = false end def run_id @run_status.run_id end def resource_current_state_loaded(new_resource, action, current_resource) unless nested_resource?(new_resource) @pending_update = ResourceReport.new_with_current_state(new_resource, action, current_resource) end end def resource_up_to_date(new_resource, action) @total_res_count += 1 @pending_update = nil unless nested_resource?(new_resource) end def resource_skipped(resource, action, conditional) @total_res_count += 1 @pending_update = nil unless nested_resource?(resource) end def resource_updated(new_resource, action) @total_res_count += 1 end def resource_failed(new_resource, action, exception) @total_res_count += 1 unless nested_resource?(new_resource) @pending_update ||= ResourceReport.new_for_exception(new_resource, action) @pending_update.exception = exception end description = Formatters::ErrorMapper.resource_failed(new_resource, action, exception) @error_descriptions = description.for_json end def resource_completed(new_resource) if @pending_update && !nested_resource?(new_resource) @pending_update.finish @updated_resources << @pending_update @pending_update = nil end end def run_completed(node) @status = "success" post_reporting_data end def run_failed(exception) @exception = exception @status = "failure" # If we failed before we received the run_started callback, there's not much we can do # in terms of reporting if @run_status post_reporting_data end end def post_reporting_data if reporting_enabled? run_data = prepare_run_data resource_history_url = "reports/nodes/#{node_name}/runs/#{run_id}" Chef::Log.info("Sending resource update report (run-id: #{run_id})") Chef::Log.debug run_data.inspect compressed_data = encode_gzip(Chef::JSONCompat.to_json(run_data)) Chef::Log.debug("Sending compressed run data...") # Since we're posting compressed data we can not directly call post_rest which expects JSON reporting_url = @rest_client.create_url(resource_history_url) begin @rest_client.raw_http_request(:POST, reporting_url, headers({'Content-Encoding' => 'gzip'}), compressed_data) rescue StandardError => e if e.respond_to? :response Chef::FileCache.store("failed-reporting-data.json", Chef::JSONCompat.to_json_pretty(run_data), 0640) Chef::Log.error("Failed to post reporting data to server (HTTP #{e.response.code}), saving to #{Chef::FileCache.load("failed-reporting-data.json", false)}") else Chef::Log.error("Failed to post reporting data to server (#{e})") end end else Chef::Log.debug("Server doesn't support resource history, skipping resource report.") end end def headers(additional_headers = {}) options = {'X-Ops-Reporting-Protocol-Version' => PROTOCOL_VERSION} options.merge(additional_headers) end def node_name @run_status.node.name end def start_time @run_status.start_time end def end_time @run_status.end_time end def prepare_run_data run_data = {} run_data["action"] = "end" run_data["resources"] = updated_resources.map do |resource_record| resource_record.for_json end run_data["status"] = @status run_data["run_list"] = Chef::JSONCompat.to_json(@run_status.node.run_list) run_data["total_res_count"] = @total_res_count.to_s run_data["data"] = {} run_data["start_time"] = start_time.to_s run_data["end_time"] = end_time.to_s if exception exception_data = {} exception_data["class"] = exception.inspect exception_data["message"] = exception.message exception_data["backtrace"] = Chef::JSONCompat.to_json(exception.backtrace) exception_data["description"] = @error_descriptions run_data["data"]["exception"] = exception_data end run_data end def run_list_expand_failed(node, exception) description = Formatters::ErrorMapper.run_list_expand_failed(node, exception) @error_descriptions = description.for_json end def cookbook_resolution_failed(expanded_run_list, exception) description = Formatters::ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception) @error_descriptions = description.for_json end def cookbook_sync_failed(cookbooks, exception) description = Formatters::ErrorMapper.cookbook_sync_failed(cookbooks, exception) @error_descriptions = description.for_json end def reporting_enabled? @reporting_enabled end private # If we are getting messages about a resource while we are in the middle of # another resource's update, we assume that the nested resource is just the # implementation of a provider, and we want to hide it from the reporting # output. def nested_resource?(new_resource) @pending_update && @pending_update.new_resource != new_resource end def encode_gzip(data) "".tap do |out| Zlib::GzipWriter.wrap(StringIO.new(out)){|gz| gz << data } end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item.rb0000644000004100000410000001122512520074675021360 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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 'chef/config' require 'chef/data_bag_item' require 'chef/encrypted_data_bag_item/decryptor' require 'chef/encrypted_data_bag_item/encryptor' require 'open-uri' # An EncryptedDataBagItem represents a read-only data bag item where # all values, except for the value associated with the id key, have # been encrypted. # # EncryptedDataBagItem can be used in recipes to decrypt data bag item # members. # # Data bag item values are assumed to have been encrypted using the # default symmetric encryption provided by Encryptor.encrypt where # values are converted to YAML prior to encryption. # # If the shared secret is not specified at initialization or load, # then the contents of the file referred to in # Chef::Config[:encrypted_data_bag_secret] will be used as the # secret. The default path is /etc/chef/encrypted_data_bag_secret # # EncryptedDataBagItem is intended to provide a means to avoid storing # data bag items in the clear on the Chef server. This provides some # protection against a breach of the Chef server or of Chef server # backup data. Because the secret must be stored in the clear on any # node needing access to an EncryptedDataBagItem, this approach # provides no protection of data bag items from actors with access to # such nodes in the infrastructure. # class Chef::EncryptedDataBagItem ALGORITHM = 'aes-256-cbc' AEAD_ALGORITHM = 'aes-256-gcm' # # === Synopsis # # EncryptedDataBagItem.new(hash, secret) # # === Args # # +enc_hash+:: # The encrypted hash to be decrypted # +secret+:: # The raw secret key # # === Description # # Create a new encrypted data bag item for reading (decryption) # def initialize(enc_hash, secret) @enc_hash = enc_hash @secret = secret end def [](key) value = @enc_hash[key] if key == "id" || value.nil? value else Decryptor.for(value, @secret).for_decrypted_item end end def []=(key, value) raise ArgumentError, "assignment not supported for #{self.class}" end def to_hash @enc_hash.keys.inject({}) { |hash, key| hash[key] = self[key]; hash } end def self.encrypt_data_bag_item(plain_hash, secret) plain_hash.inject({}) do |h, (key, val)| h[key] = if key != "id" Encryptor.new(val, secret).for_encrypted_item else val end h end end # # === Synopsis # # EncryptedDataBagItem.load(data_bag, name, secret = nil) # # === Args # # +data_bag+:: # The name of the data bag to fetch # +name+:: # The name of the data bag item to fetch # +secret+:: # The raw secret key. If the +secret+ is nil, the value of the file at # +Chef::Config[:encrypted_data_bag_secret]+ is loaded. See +load_secret+ # for more information. # # === Description # # Loads and decrypts the data bag item with the given name. # def self.load(data_bag, name, secret = nil) raw_hash = Chef::DataBagItem.load(data_bag, name) secret = secret || self.load_secret self.new(raw_hash, secret) end def self.load_secret(path=nil) path ||= Chef::Config[:encrypted_data_bag_secret] if !path raise ArgumentError, "No secret specified and no secret found at #{Chef::Config.platform_specific_path('/etc/chef/encrypted_data_bag_secret')}" end secret = case path when /^\w+:\/\// # We have a remote key begin Kernel.open(path).read.strip rescue Errno::ECONNREFUSED raise ArgumentError, "Remote key not available from '#{path}'" rescue OpenURI::HTTPError raise ArgumentError, "Remote key not found at '#{path}'" end else if !File.exist?(path) raise Errno::ENOENT, "file not found '#{path}'" end IO.read(path).strip end if secret.size < 1 raise ArgumentError, "invalid zero length secret in '#{path}'" end secret end end chef-12.3.0/lib/chef/resource_collection.rb0000644000004100000410000000627512520074675020616 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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/resource_collection/resource_set' require 'chef/resource_collection/resource_list' require 'chef/resource_collection/resource_collection_serialization' require 'chef/log' require 'forwardable' ## # ResourceCollection currently handles two tasks: # 1) Keeps an ordered list of resources to use when converging the node # 2) Keeps a unique list of resources (keyed as `type[name]`) used for notifications class Chef class ResourceCollection include ResourceCollectionSerialization extend Forwardable attr_reader :resource_set, :resource_list private :resource_set, :resource_list def initialize @resource_set = ResourceSet.new @resource_list = ResourceList.new end # @param resource [Chef::Resource] The resource to insert # @param resource_type [String,Symbol] If known, the resource type used in the recipe, Eg `package`, `execute` # @param instance_name [String] If known, the recource name as used in the recipe, IE `vim` in `package 'vim'` # This method is meant to be the 1 insert method necessary in the future. It should support all known use cases # for writing into the ResourceCollection. def insert(resource, opts={}) resource_type ||= opts[:resource_type] # Would rather use Ruby 2.x syntax, but oh well instance_name ||= opts[:instance_name] resource_list.insert(resource) if !(resource_type.nil? && instance_name.nil?) resource_set.insert_as(resource, resource_type, instance_name) else resource_set.insert_as(resource) end end # @deprecated def []=(index, resource) Chef::Log.warn("`[]=` is deprecated, use `insert` (which only inserts at the end)") resource_list[index] = resource resource_set.insert_as(resource) end # @deprecated def push(*resources) Chef::Log.warn("`push` is deprecated, use `insert`") resources.flatten.each do |res| insert(res) end self end # @deprecated alias_method :<<, :insert # Read-only methods are simple to delegate - doing that below resource_list_methods = Enumerable.instance_methods + [:iterator, :all_resources, :[], :each, :execute_each_resource, :each_index, :empty?] - [:find] # find needs to run on the set resource_set_methods = [:lookup, :find, :resources, :keys, :validate_lookup_spec!] def_delegators :resource_list, *resource_list_methods def_delegators :resource_set, *resource_set_methods end end chef-12.3.0/lib/chef/local_mode.rb0000644000004100000410000000704712520074675016650 0ustar www-datawww-data# # Author:: John Keiser () # 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/config' class Chef module LocalMode # Create a chef local server (if the configuration requires one) for the # duration of the given block. # # # This ... # with_server_connectivity { stuff } # # # Is exactly equivalent to this ... # Chef::LocalMode.setup_server_connectivity # begin # stuff # ensure # Chef::LocalMode.destroy_server_connectivity # end # def self.with_server_connectivity setup_server_connectivity begin yield ensure destroy_server_connectivity end end # If Chef::Config.chef_zero.enabled is true, sets up a chef-zero server # according to the Chef::Config.chef_zero and path options, and sets # chef_server_url to point at it. def self.setup_server_connectivity if Chef::Config.chef_zero.enabled destroy_server_connectivity require 'chef_zero/server' require 'chef/chef_fs/chef_fs_data_store' require 'chef/chef_fs/config' @chef_fs = Chef::ChefFS::Config.new.local_fs @chef_fs.write_pretty_json = true data_store = Chef::ChefFS::ChefFSDataStore.new(@chef_fs) data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, 'chef') server_options = {} server_options[:data_store] = data_store server_options[:log_level] = Chef::Log.level server_options[:host] = Chef::Config.chef_zero.host server_options[:port] = parse_port(Chef::Config.chef_zero.port) @chef_zero_server = ChefZero::Server.new(server_options) if Chef::Config[:listen] @chef_zero_server.start_background else @chef_zero_server.start_socketless end local_mode_url = @chef_zero_server.local_mode_url Chef::Log.info("Started chef-zero at #{local_mode_url} with #{@chef_fs.fs_description}") Chef::Config.chef_server_url = local_mode_url end end # Return the current chef-zero server set up by setup_server_connectivity. def self.chef_zero_server @chef_zero_server end # Return the chef_fs object for the current chef-zero server. def self.chef_fs @chef_fs end # If chef_zero_server is non-nil, stop it and remove references to it. def self.destroy_server_connectivity if @chef_zero_server @chef_zero_server.stop @chef_zero_server = nil end end def self.parse_port(port) if port.is_a?(String) parts = port.split(',') if parts.size == 1 a,b = parts[0].split('-',2) if b a.to_i.upto(b.to_i) else [ a.to_i ] end else array = [] parts.each do |part| array += parse_port(part).to_a end array end else port end end end end chef-12.3.0/lib/chef/shell.rb0000644000004100000410000002221112520074675015647 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 'singleton' require 'pp' require 'etc' require 'mixlib/cli' require 'chef' require 'chef/version' require 'chef/client' require 'chef/config' require 'chef/config_fetcher' require 'chef/shell/shell_session' require 'chef/shell/ext' require 'chef/json_compat' require 'chef/util/path_helper' # = Shell # Shell is Chef in an IRB session. Shell can interact with a Chef server via the # REST API, and run and debug recipes interactively. module Shell LEADERS = Hash.new("") LEADERS[Chef::Recipe] = ":recipe" LEADERS[Chef::Node] = ":attributes" class << self attr_accessor :client_type attr_accessor :options attr_accessor :env attr_writer :editor end # Start the irb REPL with chef-shell's customizations def self.start setup_logger # FUGLY HACK: irb gives us no other choice. irb_help = [:help, :irb_help, IRB::ExtendCommandBundle::NO_OVERRIDE] IRB::ExtendCommandBundle.instance_variable_get(:@ALIASES).delete(irb_help) parse_opts Chef::Config[:shell_config] = options.config # HACK: this duplicates the functions of IRB.start, but we have to do it # to get access to the main object before irb starts. ::IRB.setup(nil) irb = IRB::Irb.new init(irb.context.main) irb_conf[:IRB_RC].call(irb.context) if irb_conf[:IRB_RC] irb_conf[:MAIN_CONTEXT] = irb.context trap("SIGINT") do irb.signal_handle end catch(:IRB_EXIT) do irb.eval_input end end def self.setup_logger Chef::Config[:log_level] ||= :warn # If log_level is auto, change it to warn Chef::Config[:log_level] = :warn if Chef::Config[:log_level] == :auto Chef::Log.init(STDERR) Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger Chef::Log.level = Chef::Config[:log_level] || :warn end # Shell assumes it's running whenever it is defined def self.running? true end # Set the irb_conf object to something other than IRB.conf # usful for testing. def self.irb_conf=(conf_hash) @irb_conf = conf_hash end def self.irb_conf @irb_conf || IRB.conf end def self.configure_irb irb_conf[:HISTORY_FILE] = Chef::Util::PathHelper.home(".chef", "chef_shell_history") irb_conf[:SAVE_HISTORY] = 1000 irb_conf[:IRB_RC] = lambda do |conf| m = conf.main conf.prompt_c = "chef#{leader(m)} > " conf.return_format = " => %s \n" conf.prompt_i = "chef#{leader(m)} > " conf.prompt_n = "chef#{leader(m)} ?> " conf.prompt_s = "chef#{leader(m)}%l> " conf.use_tracer = false end end def self.leader(main_object) env_string = Shell.env ? " (#{Shell.env})" : "" LEADERS[main_object.class] + env_string end def self.session unless client_type.instance.node_built? puts "Session type: #{client_type.session_type}" client_type.instance.reset! end client_type.instance end def self.init(main) parse_json configure_irb session # trigger ohai run + session load session.node.consume_attributes(@json_attribs) Extensions.extend_context_object(main) main.version puts puts "run `help' for help, `exit' or ^D to quit." puts puts "Ohai2u#{greeting}!" end def self.greeting " #{Etc.getlogin}@#{Shell.session.node.fqdn}" rescue NameError, ArgumentError "" end def self.parse_json if Chef::Config[:json_attribs] config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @json_attribs = config_fetcher.fetch_json end end def self.fatal!(message, exit_status) Chef::Log.fatal(message) exit exit_status end def self.client_type type = Shell::StandAloneSession type = Shell::SoloSession if Chef::Config[:shell_solo] type = Shell::ClientSession if Chef::Config[:client] type = Shell::DoppelGangerSession if Chef::Config[:doppelganger] type end def self.parse_opts @options = Options.new @options.parse_opts end def self.editor @editor || Chef::Config[:editor] || ENV['EDITOR'] end class Options include Mixlib::CLI def self.footer(text=nil) @footer = text if text @footer end banner("chef-shell #{Chef::VERSION}\n\nUsage: chef-shell [NAMED_CONF] (OPTIONS)") footer(<<-FOOTER) When no CONFIG is specified, chef-shell attempts to load a default configuration file: * If a NAMED_CONF is given, chef-shell will load ~/.chef/NAMED_CONF/chef_shell.rb * If no NAMED_CONF is given chef-shell will load ~/.chef/chef_shell.rb if it exists * chef-shell falls back to loading /etc/chef/client.rb or /etc/chef/solo.rb if -z or -s options are given and no chef_shell.rb can be found. FOOTER option :config_file, :short => "-c CONFIG", :long => "--config CONFIG", :description => "The configuration file to use" option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true, :proc => proc { print_help } option :log_level, :short => "-l LOG_LEVEL", :long => '--log-level LOG_LEVEL', :description => "Set the logging level", :proc => proc { |level| Chef::Config.log_level = level.to_sym; Shell.setup_logger } option :standalone, :short => "-a", :long => "--standalone", :description => "standalone session", :default => true, :boolean => true option :shell_solo, :short => "-s", :long => "--solo", :description => "chef-solo session", :boolean => true, :proc => proc {Chef::Config[:solo] = true} option :client, :short => "-z", :long => "--client", :description => "chef-client session", :boolean => true option :json_attribs, :short => "-j JSON_ATTRIBS", :long => "--json-attributes JSON_ATTRIBS", :description => "Load attributes from a JSON file or URL", :proc => nil option :chef_server_url, :short => "-S CHEFSERVERURL", :long => "--server CHEFSERVERURL", :description => "The chef server URL", :proc => nil option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 option :override_runlist, :short => "-o RunlistItem,RunlistItem...", :long => "--override-runlist RunlistItem,RunlistItem...", :description => "Replace current run list with specified items", :proc => lambda { |items| items.split(',').map { |item| Chef::RunList::RunListItem.new(item) }} def self.print_help instance = new instance.parse_options([]) puts instance.opt_parser puts puts footer puts exit 1 end def self.setup! self.new.parse_opts end def parse_opts remainder = parse_options environment = remainder.first # We have to nuke ARGV to make sure irb's option parser never sees it. # otherwise, IRB complains about command line switches it doesn't recognize. ARGV.clear config[:config_file] = config_file_for_shell_mode(environment) config_msg = config[:config_file] || "none (standalone session)" puts "loading configuration: #{config_msg}" Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file]) Chef::Config.merge!(config) end private def config_file_for_shell_mode(environment) dot_chef_dir = Chef::Util::PathHelper.home('.chef') if config[:config_file] config[:config_file] elsif environment Shell.env = environment config_file_to_try = ::File.join(dot_chef_dir, environment, 'chef_shell.rb') unless ::File.exist?(config_file_to_try) puts "could not find chef-shell config for environment #{environment} at #{config_file_to_try}" exit 1 end config_file_to_try elsif dot_chef_dir && ::File.exist?(File.join(dot_chef_dir, 'chef_shell.rb')) File.join(dot_chef_dir, 'chef_shell.rb') elsif config[:solo] Chef::Config.platform_specific_path("/etc/chef/solo.rb") elsif config[:client] Chef::Config.platform_specific_path("/etc/chef/client.rb") else nil end end end end chef-12.3.0/lib/chef/user.rb0000644000004100000410000001172012520074675015521 0ustar www-datawww-data# # 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) Chef::JSONCompat.to_json(to_hash, *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 = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest('users') users = if response.is_a?(Array) transform_ohc_list_response(response) # OHC/OPC else response # OSC end if inflate users.inject({}) do |user_map, (name, _url)| user_map[name] = Chef::User.load(name) user_map end else users 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 # 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 private_class_method :transform_ohc_list_response end end chef-12.3.0/lib/chef/policy_builder/0000755000004100000410000000000012520074675017222 5ustar www-datawww-datachef-12.3.0/lib/chef/policy_builder/expand_node_object.rb0000644000004100000410000002166012520074675023366 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright 2008-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/log' require 'chef/rest' require 'chef/run_context' require 'chef/config' require 'chef/node' require 'chef/chef_class' class Chef module PolicyBuilder # ExpandNodeObject is the "classic" policy builder implementation. It # expands the run_list on a node object and then queries the chef-server # to find the correct set of cookbooks, given version constraints of the # node's environment. class ExpandNodeObject attr_reader :events attr_reader :node attr_reader :node_name attr_reader :ohai_data attr_reader :json_attribs attr_reader :override_runlist attr_reader :run_context attr_reader :run_list_expansion def initialize(node_name, ohai_data, json_attribs, override_runlist, events) @node_name = node_name @ohai_data = ohai_data @json_attribs = json_attribs @override_runlist = override_runlist @events = events @node = nil @run_list_expansion = nil end # This method injects the run_context and provider and resource priority # maps into the Chef class. The run_context has to be injected here, the provider and # resource maps could be moved if a better place can be found to do this work. # # @param run_context [Chef::RunContext] the run_context to inject def setup_chef_class(run_context) Chef.set_run_context(run_context) end def setup_run_context(specific_recipes=nil) if Chef::Config[:solo] Chef::Cookbook::FileVendor.fetch_from_disk(Chef::Config[:cookbook_path]) cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) run_context = Chef::RunContext.new(node, cookbook_collection, @events) else Chef::Cookbook::FileVendor.fetch_from_remote(api_service) cookbook_hash = sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) run_context = Chef::RunContext.new(node, cookbook_collection, @events) end # TODO: this is really obviously not the place for this # FIXME: need same edits setup_chef_class(run_context) # TODO: this is not the place for this. It should be in Runner or # CookbookCompiler or something. run_context.load(@run_list_expansion) if specific_recipes specific_recipes.each do |recipe_file| run_context.load_recipe_file(recipe_file) end end run_context end # In client-server operation, loads the node state from the server. In # chef-solo operation, builds a new node object. def load_node events.node_load_start(node_name, Chef::Config) Chef::Log.debug("Building node object for #{node_name}") if Chef::Config[:solo] @node = Chef::Node.build(node_name) else @node = Chef::Node.find_or_create(node_name) end rescue Exception => e # TODO: wrap this exception so useful error info can be given to the # user. events.node_load_failed(node_name, e, Chef::Config) raise end # Applies environment, external JSON attributes, and override run list to # the node, Then expands the run_list. # # === Returns # node:: The modified node object. node is modified in place. def build_node # Allow user to override the environment of a node by specifying # a config parameter. if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty? node.chef_environment(Chef::Config[:environment]) end # consume_external_attrs may add items to the run_list. Save the # expanded run_list, which we will pass to the server later to # determine which versions of cookbooks to use. node.reset_defaults_and_overrides node.consume_external_attrs(ohai_data, @json_attribs) setup_run_list_override expand_run_list Chef::Log.info("Run List is [#{node.run_list}]") Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]") events.node_load_completed(node, @expanded_run_list_with_versions, Chef::Config) node end # Expands the node's run list. Stores the run_list_expansion object for later use. def expand_run_list @run_list_expansion = if Chef::Config[:solo] node.expand!('disk') else node.expand!('server') end # @run_list_expansion is a RunListExpansion. # # Convert @expanded_run_list, which is an # Array of Hashes of the form # {:name => NAME, :version_constraint => Chef::VersionConstraint }, # into @expanded_run_list_with_versions, an # Array of Strings of the form # "#{NAME}@#{VERSION}" @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings @run_list_expansion rescue Exception => e # TODO: wrap/munge exception with useful error output. events.run_list_expand_failed(node, e) raise end # Sync_cookbooks eagerly loads all files except files and # templates. It returns the cookbook_hash -- the return result # from /environments/#{node.chef_environment}/cookbook_versions, # which we will use for our run_context. # # === Returns # Hash:: The hash of cookbooks with download URLs as given by the server def sync_cookbooks Chef::Log.debug("Synchronizing cookbooks") begin events.cookbook_resolution_start(@expanded_run_list_with_versions) cookbook_hash = api_service.post("environments/#{node.chef_environment}/cookbook_versions", {:run_list => @expanded_run_list_with_versions}) rescue Exception => e # TODO: wrap/munge exception to provide helpful error output events.cookbook_resolution_failed(@expanded_run_list_with_versions, e) raise else events.cookbook_resolution_complete(cookbook_hash) end synchronizer = Chef::CookbookSynchronizer.new(cookbook_hash, events) if temporary_policy? synchronizer.remove_obsoleted_files = false end synchronizer.sync_cookbooks # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks") cookbook_hash end # Indicates whether the policy is temporary, which means an # override_runlist was provided. Chef::Client uses this to decide whether # to do the final node save at the end of the run or not. def temporary_policy? !node.override_runlist.empty? end ######################################## # Internal public API ######################################## def setup_run_list_override runlist_override_sanity_check! unless(override_runlist.empty?) node.override_runlist(*override_runlist) Chef::Log.warn "Run List override has been provided." Chef::Log.warn "Original Run List: [#{node.primary_runlist}]" Chef::Log.warn "Overridden Run List: [#{node.run_list}]" end end # Ensures runlist override contains RunListItem instances def runlist_override_sanity_check! # Convert to array and remove whitespace if override_runlist.is_a?(String) @override_runlist = override_runlist.split(',').map { |e| e.strip } end @override_runlist = [override_runlist].flatten.compact override_runlist.map! do |item| if(item.is_a?(Chef::RunList::RunListItem)) item else Chef::RunList::RunListItem.new(item) end end end def api_service @api_service ||= Chef::REST.new(config[:chef_server_url]) end def config Chef::Config end end end end chef-12.3.0/lib/chef/policy_builder/policyfile.rb0000644000004100000410000003154112520074675021712 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright 2008-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/log' require 'chef/rest' require 'chef/run_context' require 'chef/config' require 'chef/node' class Chef module PolicyBuilder # Policyfile is an experimental policy builder implementation that gets run # list and cookbook version information from a single document. # # == WARNING # This implementation is experimental. It may be changed in incompatible # ways in minor or even patch releases, or even abandoned altogether. If # using this with other tools, you may be forced to upgrade those tools in # lockstep with chef-client because of incompatible behavior changes. # # == Unsupported Options: # * override_runlist:: This could potentially be integrated into the # policyfile, or replaced with a similar feature that has different # semantics. # * specific_recipes:: put more design thought into this use case. # * run_list in json_attribs:: would be ignored anyway, so it raises an error. # * chef-solo:: not currently supported. Need more design thought around # how this should work. class Policyfile class UnsupportedFeature < StandardError; end class PolicyfileError < StandardError; end RunListExpansionIsh = Struct.new(:recipes, :roles) attr_reader :events attr_reader :node attr_reader :node_name attr_reader :ohai_data attr_reader :json_attribs attr_reader :run_context def initialize(node_name, ohai_data, json_attribs, override_runlist, events) @node_name = node_name @ohai_data = ohai_data @json_attribs = json_attribs @events = events @node = nil Chef::Log.warn("Using experimental Policyfile feature") if Chef::Config[:solo] raise UnsupportedFeature, "Policyfile does not support chef-solo at this time." end if override_runlist raise UnsupportedFeature, "Policyfile does not support override run lists at this time" end if json_attribs && json_attribs.key?("run_list") raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data at this time" end if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty? raise UnsupportedFeature, "Policyfile does not work with Chef Environments" end end ## API Compat ## # Methods related to unsupported features # Override run_list is not supported. def original_runlist nil end # Override run_list is not supported. def override_runlist nil end # Policyfile gives you the run_list already expanded, but users of this # class may expect to get a run_list expansion compatible object by # calling this method. # # === Returns # RunListExpansionIsh:: A RunListExpansion duck type def run_list_expansion run_list_expansion_ish end ## PolicyBuilder API ## # Loads the node state from the server. def load_node events.node_load_start(node_name, Chef::Config) Chef::Log.debug("Building node object for #{node_name}") @node = Chef::Node.find_or_create(node_name) validate_policyfile node rescue Exception => e events.node_load_failed(node_name, e, Chef::Config) raise end # Applies environment, external JSON attributes, and override run list to # the node, Then expands the run_list. # # === Returns # node:: The modified node object. node is modified in place. def build_node # consume_external_attrs may add items to the run_list. Save the # expanded run_list, which we will pass to the server later to # determine which versions of cookbooks to use. node.reset_defaults_and_overrides node.consume_external_attrs(ohai_data, json_attribs) expand_run_list apply_policyfile_attributes Chef::Log.info("Run List is [#{run_list}]") Chef::Log.info("Run List expands to [#{run_list_with_versions_for_display.join(', ')}]") events.node_load_completed(node, run_list_with_versions_for_display, Chef::Config) node rescue Exception => e events.node_load_failed(node_name, e, Chef::Config) raise end def setup_run_context(specific_recipes=nil) Chef::Cookbook::FileVendor.fetch_from_remote(http_api) sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync) run_context = Chef::RunContext.new(node, cookbook_collection, events) run_context.load(run_list_expansion_ish) run_context end def expand_run_list node.run_list(run_list) node.automatic_attrs[:roles] = [] node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes run_list_expansion_ish end def sync_cookbooks Chef::Log.debug("Synchronizing cookbooks") synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events) synchronizer.sync_cookbooks # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks") cookbooks_to_sync end # Whether or not this is a temporary policy. Since PolicyBuilder doesn't # support override_runlist, this is always false. def temporary_policy? false end ## Internal Public API ## def run_list_with_versions_for_display run_list.map do |recipe_spec| cookbook, recipe = parse_recipe_spec(recipe_spec) lock_data = cookbook_lock_for(cookbook) display = "#{cookbook}::#{recipe}@#{lock_data["version"]} (#{lock_data["identifier"][0...7]})" display end end def run_list_expansion_ish recipes = run_list.map do |recipe_spec| cookbook, recipe = parse_recipe_spec(recipe_spec) "#{cookbook}::#{recipe}" end RunListExpansionIsh.new(recipes, []) end def apply_policyfile_attributes node.attributes.role_default = policy["default_attributes"] node.attributes.role_override = policy["override_attributes"] end def parse_recipe_spec(recipe_spec) rmatch = recipe_spec.match(/recipe\[([^:]+)::([^:]+)\]/) if rmatch.nil? raise PolicyfileError, "invalid recipe specification #{recipe_spec} in Policyfile from #{policyfile_location}" else [rmatch[1], rmatch[2]] end end def cookbook_lock_for(cookbook_name) cookbook_locks[cookbook_name] end def run_list policy["run_list"] end def policy @policy ||= http_api.get(policyfile_location) rescue Net::HTTPServerException => e raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}" end def policyfile_location if Chef::Config[:policy_document_native_api] validate_policy_config! "policy_groups/#{policy_group}/policies/#{policy_name}" else "data/policyfiles/#{deployment_group}" end end # Do some mimimal validation of the policyfile we fetched from the # server. Compatibility mode relies on using data bags to store policy # files; therefore no real validation will be performed server-side and # we need to make additional checks to ensure the data will be formatted # correctly. def validate_policyfile errors = [] unless run_list errors << "Policyfile is missing run_list element" end unless policy.key?("cookbook_locks") errors << "Policyfile is missing cookbook_locks element" end if run_list.kind_of?(Array) run_list_errors = run_list.select do |maybe_recipe_spec| validate_recipe_spec(maybe_recipe_spec) end errors += run_list_errors else errors << "Policyfile run_list is malformed, must be an array of `recipe[cb_name::recipe_name]` items: #{policy["run_list"]}" end unless errors.empty? raise PolicyfileError, "Policyfile fetched from #{policyfile_location} was invalid:\n#{errors.join("\n")}" end end def validate_recipe_spec(recipe_spec) parse_recipe_spec(recipe_spec) nil rescue PolicyfileError => e e.message end class ConfigurationError < StandardError; end def deployment_group Chef::Config[:deployment_group] or raise ConfigurationError, "Setting `deployment_group` is not configured." end def validate_policy_config! policy_group or raise ConfigurationError, "Setting `policy_group` is not configured." policy_name or raise ConfigurationError, "Setting `policy_name` is not configured." end def policy_group Chef::Config[:policy_group] end def policy_name Chef::Config[:policy_name] end # Builds a 'cookbook_hash' map of the form # "COOKBOOK_NAME" => "IDENTIFIER" # # This can be passed to a Chef::CookbookSynchronizer object to # synchronize the cookbooks. # # TODO: Currently this makes N API calls to the server to get the # cookbook objects. With server support (bulk API or the like), this # should be reduced to a single call. def cookbooks_to_sync @cookbook_to_sync ||= begin events.cookbook_resolution_start(run_list_with_versions_for_display) cookbook_versions_by_name = cookbook_locks.inject({}) do |cb_map, (name, lock_data)| cb_map[name] = manifest_for(name, lock_data) cb_map end events.cookbook_resolution_complete(cookbook_versions_by_name) cookbook_versions_by_name end rescue Exception => e # TODO: wrap/munge exception to provide helpful error output events.cookbook_resolution_failed(run_list_with_versions_for_display, e) raise end # Fetches the CookbookVersion object for the given name and identifer # specified in the lock_data. # TODO: This only implements Chef 11 compatibility mode, which means that # cookbooks are fetched by the "dotted_decimal_identifier": a # representation of a SHA1 in the traditional x.y.z version format. def manifest_for(cookbook_name, lock_data) if Chef::Config[:policy_document_native_api] artifact_manifest_for(cookbook_name, lock_data) else compat_mode_manifest_for(cookbook_name, lock_data) end end def cookbook_locks policy["cookbook_locks"] end def http_api @api_service ||= Chef::REST.new(config[:chef_server_url]) end def config Chef::Config end private def compat_mode_manifest_for(cookbook_name, lock_data) xyz_version = lock_data["dotted_decimal_identifier"] rel_url = "cookbooks/#{cookbook_name}/#{xyz_version}" http_api.get(rel_url) rescue Exception => e message = "Error loading cookbook #{cookbook_name} at version #{xyz_version} from #{rel_url}: #{e.class} - #{e.message}" err = Chef::Exceptions::CookbookNotFound.new(message) err.set_backtrace(e.backtrace) raise err end def artifact_manifest_for(cookbook_name, lock_data) identifier = lock_data["identifier"] rel_url = "cookbook_artifacts/#{cookbook_name}/#{identifier}" inflate_cbv_object(http_api.get(rel_url)) rescue Exception => e message = "Error loading cookbook #{cookbook_name} with identifier #{identifier} from #{rel_url}: #{e.class} - #{e.message}" err = Chef::Exceptions::CookbookNotFound.new(message) err.set_backtrace(e.backtrace) raise err end def inflate_cbv_object(raw_manifest) Chef::CookbookVersion.from_cb_artifact_data(raw_manifest) end end end end chef-12.3.0/lib/chef/version_constraint.rb0000644000004100000410000000701012520074675020471 0ustar www-datawww-data# Author:: Seth Falcon () # Author:: Christopher Walters () # Copyright:: Copyright 2010-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 'chef/version_class' class Chef class VersionConstraint DEFAULT_CONSTRAINT = ">= 0.0.0" STANDARD_OPS = %w(< > <= >=) OPS = %w(< > = <= >= ~>) PATTERN = /^(#{OPS.join('|')}) *([0-9].*)$/ VERSION_CLASS = Chef::Version attr_reader :op, :version def initialize(constraint_spec=DEFAULT_CONSTRAINT) case constraint_spec when nil parse(DEFAULT_CONSTRAINT) when Array parse_from_array(constraint_spec) when String parse(constraint_spec) else msg = "VersionConstraint should be created from a String. You gave: #{constraint_spec.inspect}" raise Chef::Exceptions::InvalidVersionConstraint, msg end end def include?(v) version = if v.respond_to? :version # a CookbookVersion-like object self.class::VERSION_CLASS.new(v.version.to_s) else self.class::VERSION_CLASS.new(v.to_s) end do_op(version) end def inspect "(#{to_s})" end def to_s "#{@op} #{@raw_version}" end def eql?(o) o.class == self.class && @op == o.op && @version == o.version end alias_method :==, :eql? private def do_op(other_version) if STANDARD_OPS.include? @op other_version.send(@op.to_sym, @version) elsif @op == '=' other_version == @version elsif @op == '~>' if @missing_patch_level (other_version.major == @version.major && other_version.minor >= @version.minor) else (other_version.major == @version.major && other_version.minor == @version.minor && other_version.patch >= @version.patch) end else # should never happen raise "bad op #{@op}" end end def parse_from_array(constraint_spec) if constraint_spec.empty? parse(DEFAULT_CONSTRAINT) elsif constraint_spec.size == 1 parse(constraint_spec.first) else msg = "only one version constraint operation is supported, but you gave #{constraint_spec.size} " msg << "['#{constraint_spec.join(', ')}']" raise Chef::Exceptions::InvalidVersionConstraint, msg end end def parse(str) @missing_patch_level = false if str.index(" ").nil? && str =~ /^[0-9]/ # try for lone version, implied '=' @raw_version = str @version = self.class::VERSION_CLASS.new(@raw_version) @op = "=" elsif PATTERN.match str @op = $1 @raw_version = $2 @version = self.class::VERSION_CLASS.new(@raw_version) if @raw_version.split('.').size <= 2 @missing_patch_level = true end else raise Chef::Exceptions::InvalidVersionConstraint, "'#{str}'" end end end end chef-12.3.0/lib/chef/cookbook_loader.rb0000644000004100000410000001266612520074675017711 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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/exceptions' require 'chef/cookbook/cookbook_version_loader' require 'chef/cookbook_version' require 'chef/cookbook/chefignore' require 'chef/cookbook/metadata' # # CookbookLoader class loads the cookbooks lazily as read # class Chef class CookbookLoader attr_reader :cookbooks_by_name attr_reader :merged_cookbooks attr_reader :cookbook_paths attr_reader :metadata include Enumerable def initialize(*repo_paths) repo_paths = repo_paths.flatten raise ArgumentError, "You must specify at least one cookbook repo path" if repo_paths.empty? @cookbooks_by_name = Mash.new @loaded_cookbooks = {} @metadata = Mash.new @cookbooks_paths = Hash.new {|h,k| h[k] = []} # for deprecation warnings @chefignores = {} @repo_paths = repo_paths.map do |repo_path| repo_path = File.expand_path(repo_path) end @preloaded_cookbooks = false @loaders_by_name = {} # Used to track which cookbooks appear in multiple places in the cookbook repos # and are merged in to a single cookbook by file shadowing. This behavior is # deprecated, so users of this class may issue warnings to the user by checking # this variable @merged_cookbooks = [] end def merged_cookbook_paths # for deprecation warnings merged_cookbook_paths = {} @merged_cookbooks.each {|c| merged_cookbook_paths[c] = @cookbooks_paths[c]} merged_cookbook_paths end def load_cookbooks preload_cookbooks @loaders_by_name.each do |cookbook_name, _loaders| load_cookbook(cookbook_name) end @cookbooks_by_name end def load_cookbook(cookbook_name) preload_cookbooks return nil unless @loaders_by_name.key?(cookbook_name.to_s) cookbook_loaders_for(cookbook_name).each do |loader| loader.load next if loader.empty? @cookbooks_paths[cookbook_name] << loader.cookbook_path # for deprecation warnings if @loaded_cookbooks.key?(cookbook_name) @merged_cookbooks << cookbook_name # for deprecation warnings @loaded_cookbooks[cookbook_name].merge!(loader) else @loaded_cookbooks[cookbook_name] = loader end end if @loaded_cookbooks.has_key?(cookbook_name) cookbook_version = @loaded_cookbooks[cookbook_name].cookbook_version @cookbooks_by_name[cookbook_name] = cookbook_version @metadata[cookbook_name] = cookbook_version.metadata end @cookbooks_by_name[cookbook_name] end def [](cookbook) if @cookbooks_by_name.has_key?(cookbook.to_sym) or load_cookbook(cookbook.to_sym) @cookbooks_by_name[cookbook.to_sym] else raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook.to_s}; did you forget to add metadata to a cookbook? (http://wiki.opscode.com/display/chef/Metadata)" end end alias :fetch :[] def has_key?(cookbook_name) not self[cookbook_name.to_sym].nil? end alias :cookbook_exists? :has_key? alias :key? :has_key? def each @cookbooks_by_name.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |cname| yield(cname, @cookbooks_by_name[cname]) end end def cookbook_names @cookbooks_by_name.keys.sort end def values @cookbooks_by_name.values end alias :cookbooks :values private def preload_cookbooks return false if @preloaded_cookbooks all_directories_in_repo_paths.each do |cookbook_path| preload_cookbook(cookbook_path) end @preloaded_cookbooks = true true end def preload_cookbook(cookbook_path) repo_path = File.dirname(cookbook_path) @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path) loader = Cookbook::CookbookVersionLoader.new(cookbook_path, @chefignores[repo_path]) add_cookbook_loader(loader) end def all_directories_in_repo_paths @all_directories_in_repo_paths ||= all_files_in_repo_paths.select { |path| File.directory?(path) } end def all_files_in_repo_paths @all_files_in_repo_paths ||= begin @repo_paths.inject([]) do |all_children, repo_path| all_children += Dir[File.join(Chef::Util::PathHelper.escape_glob(repo_path), "*")] end end end def add_cookbook_loader(loader) cookbook_name = loader.cookbook_name @loaders_by_name[cookbook_name.to_s] ||= [] @loaders_by_name[cookbook_name.to_s] << loader loader end def cookbook_loaders_for(cookbook_name) @loaders_by_name[cookbook_name.to_s] end end end chef-12.3.0/lib/chef/provider/0000755000004100000410000000000012520074675016047 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/cookbook_file.rb0000644000004100000410000000306212520074675021202 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/file' require 'chef/deprecation/provider/cookbook_file' require 'chef/deprecation/warnings' class Chef class Provider class CookbookFile < Chef::Provider::File provides :cookbook_file extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::CookbookFile add_deprecation_warnings_for(Chef::Deprecation::Provider::CookbookFile.instance_methods) def initialize(new_resource, run_context) @content_class = Chef::Provider::CookbookFile::Content super end def load_current_resource @current_resource = Chef::Resource::CookbookFile.new(@new_resource.name) super end private def managing_content? return true if @new_resource.checksum return true if !@new_resource.source.nil? && @action != :create_if_missing false end end end end chef-12.3.0/lib/chef/provider/ruby_block.rb0000644000004100000410000000223512520074675020531 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen () # Copyright:: Copyright (c) 2009 Opscode # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Provider class RubyBlock < Chef::Provider provides :ruby_block def whyrun_supported? true end def load_current_resource true end def action_run converge_by("execute the ruby block #{@new_resource.name}") do @new_resource.block.call Chef::Log.info("#{@new_resource} called") end end alias :action_create :action_run end end end chef-12.3.0/lib/chef/provider/git.rb0000644000004100000410000003276212520074675017171 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/exceptions' require 'chef/log' require 'chef/provider' require 'fileutils' class Chef class Provider class Git < Chef::Provider provides :git def whyrun_supported? true end def load_current_resource @resolved_reference = nil @current_resource = Chef::Resource::Git.new(@new_resource.name) if current_revision = find_current_revision @current_resource.revision current_revision end end def define_resource_requirements # Parent directory of the target must exist. requirements.assert(:checkout, :sync) do |a| dirname = ::File.dirname(@new_resource.destination) a.assertion { ::File.directory?(dirname) } a.whyrun("Directory #{dirname} does not exist, this run will fail unless it has been previously created. Assuming it would have been created.") a.failure_message(Chef::Exceptions::MissingParentDirectory, "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{dirname} does not exist") end requirements.assert(:all_actions) do |a| a.assertion { !(@new_resource.revision =~ /^origin\//) } a.failure_message Chef::Exceptions::InvalidRemoteGitReference, "Deploying remote branches is not supported. " + "Specify the remote branch as a local branch for " + "the git repository you're deploying from " + "(ie: '#{@new_resource.revision.gsub('origin/', '')}' rather than '#{@new_resource.revision}')." end requirements.assert(:all_actions) do |a| # this can't be recovered from in why-run mode, because nothing that # we do in the course of a run is likely to create a valid target_revision # if we can't resolve it up front. a.assertion { target_revision != nil } a.failure_message Chef::Exceptions::UnresolvableGitReference, "Unable to parse SHA reference for '#{@new_resource.revision}' in repository '#{@new_resource.repository}'. " + "Verify your (case-sensitive) repository URL and revision.\n" + "`git ls-remote '#{@new_resource.repository}' '#{rev_search_pattern}'` output: #{@resolved_reference}" end end def action_checkout if target_dir_non_existent_or_empty? clone if @new_resource.enable_checkout checkout end enable_submodules add_remotes else Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory" end end def action_export action_checkout converge_by("complete the export by removing #{@new_resource.destination}.git after checkout") do FileUtils.rm_rf(::File.join(@new_resource.destination,".git")) end end def action_sync if existing_git_clone? Chef::Log.debug "#{@new_resource} current revision: #{@current_resource.revision} target revision: #{target_revision}" unless current_revision_matches_target_revision? fetch_updates enable_submodules Chef::Log.info "#{@new_resource} updated to revision #{target_revision}" end add_remotes else action_checkout end end def git_minor_version @git_minor_version ||= Gem::Version.new(shell_out!('git --version', run_options).stdout.split.last) end def existing_git_clone? ::File.exist?(::File.join(@new_resource.destination, ".git")) end def target_dir_non_existent_or_empty? !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..'] end def find_current_revision Chef::Log.debug("#{@new_resource} finding current git revision") if ::File.exist?(::File.join(cwd, ".git")) # 128 is returned when we're not in a git repo. this is fine result = shell_out!('git rev-parse HEAD', :cwd => cwd, :returns => [0,128]).stdout.strip end sha_hash?(result) ? result : nil end def add_remotes if (@new_resource.additional_remotes.length > 0) @new_resource.additional_remotes.each_pair do |remote_name, remote_url| converge_by("add remote #{remote_name} from #{remote_url}") do Chef::Log.info "#{@new_resource} adding git remote #{remote_name} = #{remote_url}" setup_remote_tracking_branches(remote_name, remote_url) end end end end def clone converge_by("clone from #{@new_resource.repository} into #{@new_resource.destination}") do remote = @new_resource.remote args = [] args << "-o #{remote}" unless remote == 'origin' args << "--depth #{@new_resource.depth}" if @new_resource.depth args << "--no-single-branch" if @new_resource.depth and git_minor_version >= Gem::Version.new('1.7.10') Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{@new_resource.destination}" clone_cmd = "git clone #{args.join(' ')} \"#{@new_resource.repository}\" \"#{@new_resource.destination}\"" shell_out!(clone_cmd, run_options) end end def checkout sha_ref = target_revision converge_by("checkout ref #{sha_ref} branch #{@new_resource.revision}") do # checkout into a local branch rather than a detached HEAD shell_out!("git branch -f #{@new_resource.checkout_branch} #{sha_ref}", run_options(:cwd => @new_resource.destination)) shell_out!("git checkout #{@new_resource.checkout_branch}", run_options(:cwd => @new_resource.destination)) Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} onto: #{@new_resource.checkout_branch} reference: #{sha_ref}" end end def enable_submodules if @new_resource.enable_submodules converge_by("enable git submodules for #{@new_resource}") do Chef::Log.info "#{@new_resource} synchronizing git submodules" command = "git submodule sync" shell_out!(command, run_options(:cwd => @new_resource.destination)) Chef::Log.info "#{@new_resource} enabling git submodules" # the --recursive flag means we require git 1.6.5+ now, see CHEF-1827 command = "git submodule update --init --recursive" shell_out!(command, run_options(:cwd => @new_resource.destination)) end end end def fetch_updates setup_remote_tracking_branches(@new_resource.remote, @new_resource.repository) converge_by("fetch updates for #{@new_resource.remote}") do # since we're in a local branch already, just reset to specified revision rather than merge fetch_command = "git fetch #{@new_resource.remote} && git fetch #{@new_resource.remote} --tags && git reset --hard #{target_revision}" Chef::Log.debug "Fetching updates from #{new_resource.remote} and resetting to revision #{target_revision}" shell_out!(fetch_command, run_options(:cwd => @new_resource.destination)) end end def setup_remote_tracking_branches(remote_name, remote_url) converge_by("set up remote tracking branches for #{remote_url} at #{remote_name}") do Chef::Log.debug "#{@new_resource} configuring remote tracking branches for repository #{remote_url} "+ "at remote #{remote_name}" check_remote_command = "git config --get remote.#{remote_name}.url" remote_status = shell_out!(check_remote_command, run_options(:cwd => @new_resource.destination, :returns => [0,1,2])) case remote_status.exitstatus when 0, 2 # * Status 0 means that we already have a remote with this name, so we should update the url # if it doesn't match the url we want. # * Status 2 means that we have multiple urls assigned to the same remote (not a good idea) # which we can fix by replacing them all with our target url (hence the --replace-all option) if multiple_remotes?(remote_status) || !remote_matches?(remote_url,remote_status) update_remote_url_command = "git config --replace-all remote.#{remote_name}.url #{remote_url}" shell_out!(update_remote_url_command, run_options(:cwd => @new_resource.destination)) end when 1 add_remote_command = "git remote add #{remote_name} #{remote_url}" shell_out!(add_remote_command, run_options(:cwd => @new_resource.destination)) end end end def multiple_remotes?(check_remote_command_result) check_remote_command_result.exitstatus == 2 end def remote_matches?(remote_url, check_remote_command_result) check_remote_command_result.stdout.strip.eql?(remote_url) end def current_revision_matches_target_revision? (!@current_resource.revision.nil?) && (target_revision.strip.to_i(16) == @current_resource.revision.strip.to_i(16)) end def target_revision @target_revision ||= begin if sha_hash?(@new_resource.revision) @target_revision = @new_resource.revision else @target_revision = remote_resolve_reference end end end alias :revision_slug :target_revision def remote_resolve_reference Chef::Log.debug("#{@new_resource} resolving remote reference") # The sha pointed to by an annotated tag is identified by the # '^{}' suffix appended to the tag. In order to resolve # annotated tags, we have to search for "revision*" and # post-process. Special handling for 'HEAD' to ignore a tag # named 'HEAD'. @resolved_reference = git_ls_remote(rev_search_pattern) refs = @resolved_reference.split("\n").map { |line| line.split("\t") } # First try for ^{} indicating the commit pointed to by an # annotated tag. # It is possible for a user to create a tag named 'HEAD'. # Using such a degenerate annotated tag would be very # confusing. We avoid the issue by disallowing the use of # annotated tags named 'HEAD'. if rev_search_pattern != 'HEAD' found = find_revision(refs, @new_resource.revision, '^{}') else found = refs_search(refs, 'HEAD') end found = find_revision(refs, @new_resource.revision) if found.empty? found.size == 1 ? found.first[0] : nil end def find_revision(refs, revision, suffix="") found = refs_search(refs, rev_match_pattern('refs/tags/', revision) + suffix) found = refs_search(refs, rev_match_pattern('refs/heads/', revision) + suffix) if found.empty? found = refs_search(refs, revision + suffix) if found.empty? found end def rev_match_pattern(prefix, revision) if revision.start_with?(prefix) revision else prefix + revision end end def rev_search_pattern if ['', 'HEAD'].include? @new_resource.revision 'HEAD' else @new_resource.revision + '*' end end def git_ls_remote(rev_pattern) command = git(%Q(ls-remote "#{@new_resource.repository}" "#{rev_pattern}")) shell_out!(command, run_options).stdout end def refs_search(refs, pattern) refs.find_all { |m| m[1] == pattern } end private def run_options(run_opts={}) env = {} if @new_resource.user run_opts[:user] = @new_resource.user # Certain versions of `git` misbehave if git configuration is # inaccessible in $HOME. We need to ensure $HOME matches the # user who is executing `git` not the user running Chef. env['HOME'] = begin require 'etc' Etc.getpwnam(@new_resource.user).dir rescue ArgumentError # user not found raise Chef::Exceptions::User, "Could not determine HOME for specified user '#{@new_resource.user}' for resource '#{@new_resource.name}'" end end run_opts[:group] = @new_resource.group if @new_resource.group env['GIT_SSH'] = @new_resource.ssh_wrapper if @new_resource.ssh_wrapper run_opts[:log_tag] = @new_resource.to_s run_opts[:timeout] = @new_resource.timeout if @new_resource.timeout env.merge!(@new_resource.environment) if @new_resource.environment run_opts[:environment] = env unless env.empty? run_opts end def cwd @new_resource.destination end def git(*args) ["git", *args].compact.join(" ") end def sha_hash?(string) string =~ /^[0-9a-f]{40}$/ end end end end chef-12.3.0/lib/chef/provider/directory.rb0000644000004100000410000001205312520074675020401 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/config' require 'chef/log' require 'chef/resource/directory' require 'chef/provider' require 'chef/provider/file' require 'fileutils' class Chef class Provider class Directory < Chef::Provider::File provides :directory def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::Directory.new(@new_resource.name) @current_resource.path(@new_resource.path) if ::File.exists?(@current_resource.path) && @action != :create_if_missing load_resource_attributes_from_file(@current_resource) end @current_resource end def define_resource_requirements requirements.assert(:create) do |a| # Make sure the parent dir exists, or else fail. # for why run, print a message explaining the potential error. parent_directory = ::File.dirname(@new_resource.path) a.assertion { @new_resource.recursive || ::File.directory?(parent_directory) } a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist, cannot create #{@new_resource.path}") a.whyrun("Assuming directory #{parent_directory} would have been created") end requirements.assert(:create) do |a| parent_directory = ::File.dirname(@new_resource.path) a.assertion do if @new_resource.recursive # find the lowest-level directory in @new_resource.path that already exists # make sure we have write permissions to that directory is_parent_writable = lambda do |base_dir| base_dir = ::File.dirname(base_dir) if ::File.exists?(base_dir) Chef::FileAccessControl.writable?(base_dir) else is_parent_writable.call(base_dir) end end is_parent_writable.call(@new_resource.path) else # in why run mode & parent directory does not exist no permissions check is required # If not in why run, permissions must be valid and we rely on prior assertion that dir exists if !whyrun_mode? || ::File.exists?(parent_directory) Chef::FileAccessControl.writable?(parent_directory) else true end end end a.failure_message(Chef::Exceptions::InsufficientPermissions, "Cannot create #{@new_resource} at #{@new_resource.path} due to insufficient permissions") end requirements.assert(:delete) do |a| a.assertion do if ::File.exists?(@new_resource.path) ::File.directory?(@new_resource.path) && Chef::FileAccessControl.writable?(@new_resource.path) else true end end a.failure_message(RuntimeError, "Cannot delete #{@new_resource} at #{@new_resource.path}!") # No why-run handling here: # * if we don't have permissions, this is unlikely to be changed earlier in the run # * if the target is a file (not a dir), there's no reasonable path by which this would have been changed end end def action_create unless ::File.exists?(@new_resource.path) converge_by("create new directory #{@new_resource.path}") do if @new_resource.recursive == true ::FileUtils.mkdir_p(@new_resource.path) else ::Dir.mkdir(@new_resource.path) end Chef::Log.info("#{@new_resource} created directory #{@new_resource.path}") end end do_acl_changes do_selinux(true) load_resource_attributes_from_file(@new_resource) end def action_delete if ::File.exists?(@new_resource.path) converge_by("delete existing directory #{@new_resource.path}") do if @new_resource.recursive == true FileUtils.rm_rf(@new_resource.path) Chef::Log.info("#{@new_resource} deleted #{@new_resource.path} recursively") else ::Dir.delete(@new_resource.path) Chef::Log.info("#{@new_resource} deleted #{@new_resource.path}") end end end end private def managing_content? false end end end end chef-12.3.0/lib/chef/provider/http_request.rb0000644000004100000410000001001612520074675021121 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'tempfile' require 'chef/http/simple' class Chef class Provider class HttpRequest < Chef::Provider provides :http_request attr_accessor :http def whyrun_supported? true end def load_current_resource @http = Chef::HTTP::Simple.new(@new_resource.url) end # Send a HEAD request to @new_resource.url def action_head message = check_message(@new_resource.message) # CHEF-4762: we expect a nil return value from Chef::HTTP for a "200 Success" response # and false for a "304 Not Modified" response modified = @http.head( "#{@new_resource.url}", @new_resource.headers ) Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful") Chef::Log.debug("#{@new_resource} HEAD request response: #{modified}") # :head is usually used to trigger notifications, which converge_by now does if modified != false converge_by("#{@new_resource} HEAD to #{@new_resource.url} returned modified, trigger notifications") {} end end # Send a GET request to @new_resource.url def action_get converge_by("#{@new_resource} GET to #{@new_resource.url}") do message = check_message(@new_resource.message) body = @http.get( "#{@new_resource.url}", @new_resource.headers ) Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful") Chef::Log.debug("#{@new_resource} GET request response: #{body}") end end # Send a PUT request to @new_resource.url, with the message as the payload def action_put converge_by("#{@new_resource} PUT to #{@new_resource.url}") do message = check_message(@new_resource.message) body = @http.put( "#{@new_resource.url}", message, @new_resource.headers ) Chef::Log.info("#{@new_resource} PUT to #{@new_resource.url} successful") Chef::Log.debug("#{@new_resource} PUT request response: #{body}") end end # Send a POST request to @new_resource.url, with the message as the payload def action_post converge_by("#{@new_resource} POST to #{@new_resource.url}") do message = check_message(@new_resource.message) body = @http.post( "#{@new_resource.url}", message, @new_resource.headers ) Chef::Log.info("#{@new_resource} POST to #{@new_resource.url} message: #{message.inspect} successful") Chef::Log.debug("#{@new_resource} POST request response: #{body}") end end # Send a DELETE request to @new_resource.url def action_delete converge_by("#{@new_resource} DELETE to #{@new_resource.url}") do body = @http.delete( "#{@new_resource.url}", @new_resource.headers ) @new_resource.updated_by_last_action(true) Chef::Log.info("#{@new_resource} DELETE to #{@new_resource.url} successful") Chef::Log.debug("#{@new_resource} DELETE request response: #{body}") end end private def check_message(message) if message.kind_of?(Proc) message.call else message end end end end end chef-12.3.0/lib/chef/provider/breakpoint.rb0000644000004100000410000000210412520074675020527 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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. # class Chef class Provider class Breakpoint < Chef::Provider provides :breakpoint def load_current_resource end def action_break if defined?(Shell) && Shell.running? run_context.resource_collection.iterator.pause @new_resource.updated_by_last_action(true) run_context.resource_collection.iterator end end end end end chef-12.3.0/lib/chef/provider/remote_directory.rb0000644000004100000410000001612112520074675021754 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/provider/file' require 'chef/provider/directory' require 'chef/resource/directory' require 'chef/resource/remote_file' require 'chef/mixin/file_class' require 'chef/platform' require 'uri' require 'tempfile' require 'net/https' require 'set' require 'chef/util/path_helper' class Chef class Provider class RemoteDirectory < Chef::Provider::Directory provides :remote_directory include Chef::Mixin::FileClass def action_create super # Mark all files as needing to be purged files_to_purge = Set.new(ls(@new_resource.path)) # Make sure each path is clean # Transfer files files_to_transfer.each do |cookbook_file_relative_path| create_cookbook_file(cookbook_file_relative_path) # parent directories and file being transferred are removed from the purge list Pathname.new(Chef::Util::PathHelper.cleanpath(::File.join(@new_resource.path, cookbook_file_relative_path))).descend do |d| files_to_purge.delete(d.to_s) end end purge_unmanaged_files(files_to_purge) end def action_create_if_missing # if this action is called, ignore the existing overwrite flag @new_resource.overwrite(false) action_create end protected # List all excluding . and .. def ls(path) files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'), ::File::FNM_DOTMATCH) # Remove current directory and previous directory files.reject! do |name| basename = Pathname.new(name).basename().to_s ['.', '..'].include?(basename) end # Clean all the paths... this is required because of the join files.map {|f| Chef::Util::PathHelper.cleanpath(f)} end def purge_unmanaged_files(unmanaged_files) if @new_resource.purge unmanaged_files.sort.reverse.each do |f| # file_class comes from Chef::Mixin::FileClass if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup) # Linux treats directory symlinks as files # Remove a directory as a directory when not on windows if it is not a symlink purge_directory(f) elsif ::File.directory?(f) && Chef::Platform.windows? # Windows treats directory symlinks as directories so we delete them here purge_directory(f) else converge_by("delete unmanaged file #{f}") do ::File.delete(f) Chef::Log.debug("#{@new_resource} deleted file #{f}") end end end end end def purge_directory(dir) converge_by("delete unmanaged directory #{dir}") do Dir::rmdir(dir) Chef::Log.debug("#{@new_resource} removed directory #{dir}") end end def files_to_transfer cookbook = run_context.cookbook_collection[resource_cookbook] files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source) files.sort.reverse end def directory_root_in_cookbook_cache @directory_root_in_cookbook_cache ||= begin cookbook = run_context.cookbook_collection[resource_cookbook] cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path) end end # Determine the cookbook to get the file from. If new resource sets an # explicit cookbook, use it, otherwise fall back to the implicit cookbook # i.e., the cookbook the resource was declared in. def resource_cookbook @new_resource.cookbook || @new_resource.cookbook_name end def create_cookbook_file(cookbook_file_relative_path) full_path = ::File.join(@new_resource.path, cookbook_file_relative_path) ensure_directory_exists(::File.dirname(full_path)) file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path) if @new_resource.overwrite file_to_fetch.run_action(:create) else file_to_fetch.run_action(:create_if_missing) end @new_resource.updated_by_last_action(true) if file_to_fetch.updated? end def cookbook_file_resource(target_path, relative_source_path) cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context) cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name cookbook_file.source(::File.join(@new_resource.source, relative_source_path)) if Chef::Platform.windows? && @new_resource.files_rights @new_resource.files_rights.each_pair do |permission, *args| cookbook_file.rights(permission, *args) end end cookbook_file.mode(@new_resource.files_mode) if @new_resource.files_mode cookbook_file.group(@new_resource.files_group) if @new_resource.files_group cookbook_file.owner(@new_resource.files_owner) if @new_resource.files_owner cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup cookbook_file end def ensure_directory_exists(path) unless ::File.directory?(path) directory_to_create = resource_for_directory(path) directory_to_create.run_action(:create) @new_resource.updated_by_last_action(true) if directory_to_create.updated? end end def resource_for_directory(path) dir = Chef::Resource::Directory.new(path, run_context) dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name if Chef::Platform.windows? && @new_resource.rights # rights are only meant to be applied to the toppest-level directory; # Windows will handle inheritance. if path == @new_resource.path @new_resource.rights.each do |rights| #rights is a hash permissions = rights.delete(:permissions) #delete will return the value or nil if not found principals = rights.delete(:principals) dir.rights(permissions, principals, rights) end end end dir.mode(@new_resource.mode) if @new_resource.mode dir.group(@new_resource.group) dir.owner(@new_resource.owner) dir.recursive(true) dir end def whyrun_supported? true end end end end chef-12.3.0/lib/chef/provider/execute.rb0000644000004100000410000000605312520074675020042 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/log' require 'chef/provider' require 'forwardable' class Chef class Provider class Execute < Chef::Provider extend Forwardable provides :execute def_delegators :@new_resource, :command, :returns, :environment, :user, :group, :cwd, :umask, :creates def load_current_resource current_resource = Chef::Resource::Execute.new(new_resource.name) current_resource end def whyrun_supported? true end def define_resource_requirements # @todo: this should change to raise in some appropriate major version bump. if creates && creates_relative? && !cwd Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail (CHEF-3819)" end end def timeout # original implementation did not specify a timeout, but ShellOut # *always* times out. So, set a very long default timeout new_resource.timeout || 3600 end def action_run if creates && sentinel_file.exist? Chef::Log.debug("#{new_resource} sentinel file #{sentinel_file} exists - nothing to do") return false end converge_by("execute #{description}") do result = shell_out!(command, opts) Chef::Log.info("#{new_resource} ran successfully") end end private def sensitive? !!new_resource.sensitive end def opts opts = {} opts[:timeout] = timeout opts[:returns] = returns if returns opts[:environment] = environment if environment opts[:user] = user if user opts[:group] = group if group opts[:cwd] = cwd if cwd opts[:umask] = umask if umask opts[:log_level] = :info opts[:log_tag] = new_resource.to_s if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info? && !sensitive? opts[:live_stream] = STDOUT end opts end def description sensitive? ? "sensitive resource" : command end def creates_relative? Pathname(creates).relative? end def sentinel_file Pathname.new(Chef::Util::PathHelper.cleanpath( ( cwd && creates_relative? ) ? ::File.join(cwd, creates) : creates )) end end end end chef-12.3.0/lib/chef/provider/ifconfig.rb0000644000004100000410000001650312520074675020165 0ustar www-datawww-data# # Author:: Jason K. Jackson (jasonjackson@gmail.com) # Copyright:: Copyright (c) 2009 Jason K. Jackson # 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/log' require 'chef/mixin/command' require 'chef/mixin/shell_out' require 'chef/provider' require 'chef/resource/file' require 'chef/exceptions' require 'erb' # Recipe example: # # int = {Hash with your network settings...} # # ifconfig int['ip'] do # ignore_failure true # device int['dev'] # mask int['mask'] # gateway int['gateway'] # mtu int['mtu'] # end class Chef class Provider class Ifconfig < Chef::Provider include Chef::Mixin::ShellOut include Chef::Mixin::Command attr_accessor :config_template attr_accessor :config_path def initialize(new_resource, run_context) super(new_resource, run_context) @config_template = nil @config_path = nil end def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name) @ifconfig_success = true @interfaces = {} @status = shell_out("ifconfig") @status.stdout.each_line do |line| if !line[0..9].strip.empty? @int_name = line[0..9].strip @interfaces[@int_name] = {"hwaddr" => (line =~ /(HWaddr)/ ? ($') : "nil").strip.chomp } else @interfaces[@int_name]["inet_addr"] = (line =~ /inet addr:(\S+)/ ? ($1) : "nil") if line =~ /inet addr:/ @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? ($1) : "nil") if line =~ /Bcast:/ @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? ($1) : "nil") if line =~ /Mask:/ @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? ($1) : "nil") if line =~ /MTU:/ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? ($1) : "nil") if line =~ /Metric:/ end if @interfaces.has_key?(@new_resource.device) @interface = @interfaces.fetch(@new_resource.device) @current_resource.target(@new_resource.target) @current_resource.device(@new_resource.device) @current_resource.inet_addr(@interface["inet_addr"]) @current_resource.hwaddr(@interface["hwaddr"]) @current_resource.bcast(@interface["bcast"]) @current_resource.mask(@interface["mask"]) @current_resource.mtu(@interface["mtu"]) @current_resource.metric(@interface["metric"]) end end @current_resource end def define_resource_requirements requirements.assert(:all_actions) do |a| a.assertion { @status.exitstatus == 0 } a.failure_message Chef::Exceptions::Ifconfig, "ifconfig failed - #{@status.inspect}!" # no whyrun - if the base ifconfig used in load_current_resource fails # there's no reasonable action that could have been taken in the course of # a chef run to fix it. end end def action_add # check to see if load_current_resource found interface in ifconfig unless @current_resource.inet_addr unless @new_resource.device == loopback_device command = add_command converge_by ("run #{command} to add #{@new_resource}") do run_command( :command => command ) Chef::Log.info("#{@new_resource} added") end end end # Write out the config files generate_config end def action_enable # check to see if load_current_resource found ifconfig # enables, but does not manage config files unless @current_resource.inet_addr unless @new_resource.device == loopback_device command = enable_command converge_by ("run #{command} to enable #{@new_resource}") do run_command( :command => command ) Chef::Log.info("#{@new_resource} enabled") end end end end def action_delete # check to see if load_current_resource found the interface if @current_resource.device command = delete_command converge_by ("run #{command} to delete #{@new_resource}") do run_command( :command => command ) Chef::Log.info("#{@new_resource} deleted") end else Chef::Log.debug("#{@new_resource} does not exist - nothing to do") end delete_config end def action_disable # check to see if load_current_resource found the interface # disables, but leaves config files in place. if @current_resource.device command = disable_command converge_by ("run #{command} to disable #{@new_resource}") do run_command( :command => command ) Chef::Log.info("#{@new_resource} disabled") end else Chef::Log.debug("#{@new_resource} does not exist - nothing to do") end end def can_generate_config? ! @config_template.nil? and ! @config_path.nil? end def resource_for_config(path) Chef::Resource::File.new(path, run_context) end def generate_config return unless can_generate_config? b = binding template = ::ERB.new(@config_template) config = resource_for_config(@config_path) config.content(template.result(b)) config.run_action(:create) @new_resource.updated_by_last_action(true) if config.updated? end def delete_config return unless can_generate_config? config = resource_for_config(@config_path) config.run_action(:delete) @new_resource.updated_by_last_action(true) if config.updated? end private def add_command command = "ifconfig #{@new_resource.device} #{@new_resource.name}" command << " netmask #{@new_resource.mask}" if @new_resource.mask command << " metric #{@new_resource.metric}" if @new_resource.metric command << " mtu #{@new_resource.mtu}" if @new_resource.mtu command end def enable_command command = "ifconfig #{@new_resource.device} #{@new_resource.name}" command << " netmask #{@new_resource.mask}" if @new_resource.mask command << " metric #{@new_resource.metric}" if @new_resource.metric command << " mtu #{@new_resource.mtu}" if @new_resource.mtu command end def disable_command "ifconfig #{@new_resource.device} down" end def delete_command "ifconfig #{@new_resource.device} down" end def loopback_device 'lo' end end end end chef-12.3.0/lib/chef/provider/mount.rb0000644000004100000410000001303212520074675017535 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2009-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/log' require 'chef/mixin/shell_out' require 'chef/provider' class Chef class Provider class Mount < Chef::Provider include Chef::Mixin::ShellOut attr_accessor :unmount_retries def whyrun_supported? true end def load_current_resource true end def initialize(new_resource, run_context) super self.unmount_retries = 20 end def action_mount unless current_resource.mounted converge_by("mount #{current_resource.device} to #{current_resource.mount_point}") do mount_fs Chef::Log.info("#{new_resource} mounted") end else Chef::Log.debug("#{new_resource} is already mounted") end end def action_umount if current_resource.mounted converge_by("unmount #{current_resource.device}") do umount_fs Chef::Log.info("#{new_resource} unmounted") end else Chef::Log.debug("#{new_resource} is already unmounted") end end def action_remount if current_resource.mounted if new_resource.supports[:remount] converge_by("remount #{current_resource.device}") do remount_fs Chef::Log.info("#{new_resource} remounted") end else converge_by("unmount #{current_resource.device}") do umount_fs Chef::Log.info("#{new_resource} unmounted") end wait_until_unmounted(unmount_retries) converge_by("mount #{current_resource.device}") do mount_fs Chef::Log.info("#{new_resource} mounted") end end else Chef::Log.debug("#{new_resource} not mounted, nothing to remount") end end def action_enable unless current_resource.enabled && mount_options_unchanged? converge_by("enable #{current_resource.device}") do enable_fs Chef::Log.info("#{new_resource} enabled") end else Chef::Log.debug("#{new_resource} already enabled") end end def action_disable if current_resource.enabled converge_by("disable #{current_resource.device}") do disable_fs Chef::Log.info("#{new_resource} disabled") end else Chef::Log.debug("#{new_resource} already disabled") end end # # Abstract Methods to be implemented by subclasses # # should actually check if the filesystem is mounted (not just return current_resource) and return true/false def mounted? raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not implement #mounted?" end # should check new_resource against current_resource to see if mount options need updating, returns true/false def mount_options_unchanged? raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not implement #mount_options_unchanged?" end # # NOTE: for the following methods, this superclass will already have checked if the filesystem is # enabled and/or mounted and they will be called in converge_by blocks, so most defensive checking # does not need to be done in the subclass implementation -- just do the thing. # # should implement mounting of the filesystem, raises if action does not succeed def mount_fs raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :mount" end # should implement unmounting of the filesystem, raises if action does not succeed def umount_fs raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :umount" end # should implement remounting of the filesystem (via a -o remount or some other atomic-ish action that isn't # simply a umount/mount style remount), raises if action does not succeed def remount_fs raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remount" end # should implement enabling of the filesystem (e.g. in /etc/fstab), raises if action does not succeed def enable_fs raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable" end # should implement disabling of the filesystem (e.g. in /etc/fstab), raises if action does not succeed def disable_fs raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable" end private def wait_until_unmounted(tries) while mounted? if (tries -= 1) < 0 raise Chef::Exceptions::Mount, "Retries exceeded waiting for filesystem to unmount" end sleep 0.1 end end end end end chef-12.3.0/lib/chef/provider/file/0000755000004100000410000000000012520074675016766 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/file/content.rb0000644000004100000410000000232112520074675020763 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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/file_content_management/content_base' require 'chef/file_content_management/tempfile' class Chef class Provider class File class Content < Chef::FileContentManagement::ContentBase def file_for_provider if @new_resource.content tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile tempfile.write(@new_resource.content) tempfile.close tempfile else nil end end end end end end chef-12.3.0/lib/chef/provider/dsc_script.rb0000644000004100000410000001537012520074675020537 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/cmdlet' require 'chef/util/dsc/configuration_generator' require 'chef/util/dsc/local_configuration_manager' require 'chef/util/path_helper' class Chef class Provider class DscScript < Chef::Provider provides :dsc_script, os: "windows" def initialize(dsc_resource, run_context) super(dsc_resource, run_context) @dsc_resource = dsc_resource @resource_converged = false @operations = { :set => Proc.new { |config_manager, document, shellout_flags| config_manager.set_configuration(document, shellout_flags) }, :test => Proc.new { |config_manager, document, shellout_flags| config_manager.test_configuration(document, shellout_flags) }} end def action_run if ! @resource_converged converge_by(generate_description) do run_configuration(:set) Chef::Log.info("DSC resource configuration completed successfully") end end end def load_current_resource if supports_dsc? @dsc_resources_info = run_configuration(:test) @resource_converged = @dsc_resources_info.all? do |resource| !resource.changes_state? end end end def whyrun_supported? true end def define_resource_requirements requirements.assert(:run) do |a| err = [ 'Could not find PowerShell DSC support on the system', powershell_info_str, "Powershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.", ] a.assertion { supports_dsc? } a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ') a.whyrun err + ["Assuming a previous resource installs Powershell 4.0 or higher."] a.block_action! end end protected def supports_dsc? run_context && Chef::Platform.supports_dsc?(node) end def run_configuration(operation) config_directory = ::Dir.mktmpdir("chef-dsc-script") configuration_data_path = get_configuration_data_path(config_directory) configuration_flags = get_augmented_configuration_flags(configuration_data_path) config_manager = Chef::Util::DSC::LocalConfigurationManager.new(@run_context.node, config_directory) shellout_flags = { :cwd => @dsc_resource.cwd, :environment => @dsc_resource.environment, :timeout => @dsc_resource.timeout } begin configuration_document = generate_configuration_document(config_directory, configuration_flags) @operations[operation].call(config_manager, configuration_document, shellout_flags) rescue Exception => e Chef::Log.error("DSC operation failed: #{e.message.to_s}") raise e ensure ::FileUtils.rm_rf(config_directory) end end def get_augmented_configuration_flags(configuration_data_path) updated_flags = @dsc_resource.flags.nil? ? {} : @dsc_resource.flags.dup if configuration_data_path Chef::Util::PathHelper.validate_path(configuration_data_path) updated_flags[:configurationdata] = configuration_data_path end updated_flags end def generate_configuration_document(config_directory, configuration_flags) shellout_flags = { :cwd => @dsc_resource.cwd, :environment => @dsc_resource.environment, :timeout => @dsc_resource.timeout } generator = Chef::Util::DSC::ConfigurationGenerator.new(@run_context.node, config_directory) if @dsc_resource.command generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags, shellout_flags) else # If code is also not provided, we mimic what the other script resources do (execute nothing) Chef::Log.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code generator.configuration_document_from_script_code(@dsc_resource.code || '', configuration_flags, @dsc_resource.imports, shellout_flags) end end def get_configuration_data_path(config_directory) if @dsc_resource.configuration_data_script @dsc_resource.configuration_data_script elsif @dsc_resource.configuration_data configuration_data_path = "#{config_directory}/chef_dsc_config_data.psd1" ::File.open(configuration_data_path, 'wt') do | script | script.write(@dsc_resource.configuration_data) end configuration_data_path end end def configuration_name @dsc_resource.configuration_name || @dsc_resource.name end def configuration_friendly_name if @dsc_resource.code @dsc_resource.name else configuration_name end end private def generate_description ["converge DSC configuration '#{configuration_friendly_name}'"] + @dsc_resources_info.map do |resource| if resource.changes_state? # We ignore the last log message because it only contains the time it took, which looks weird cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, '').strip } "converge DSC resource #{resource.name} by #{cleaned_messages.find_all{ |c| c != ''}.join("\n")}" else # This is needed because a dsc script can have resources that are both converged and not "converge DSC resource #{resource.name} by doing nothing because it is already converged" end end end def powershell_info_str if run_context && run_context.node[:languages] && run_context.node[:languages][:powershell] install_info = "Powershell #{run_context.node[:languages][:powershell][:version]} was found on the system." else install_info = 'Powershell was not found.' end end end end end chef-12.3.0/lib/chef/provider/erl_call.rb0000644000004100000410000000565412520074675020163 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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/log' require 'chef/mixin/command' require 'chef/provider' class Chef class Provider class ErlCall < Chef::Provider include Chef::Mixin::Command provides :erl_call def initialize(node, new_resource) super(node, new_resource) end def whyrun_supported? true end def load_current_resource true end def action_run case @new_resource.name_type when "sname" node = "-sname #{@new_resource.node_name}" when "name" node = "-name #{@new_resource.node_name}" end if @new_resource.cookie cookie = "-c #{@new_resource.cookie}" else cookie = "" end if @new_resource.distributed distributed = "-s" else distributed = "" end command = "erl_call -e #{distributed} #{node} #{cookie}" converge_by("run erlang block") do begin pid, stdin, stdout, stderr = popen4(command, :waitlast => true) Chef::Log.debug("#{@new_resource} running") Chef::Log.debug("#{@new_resource} command: #{command}") Chef::Log.debug("#{@new_resource} code: #{@new_resource.code}") @new_resource.code.each_line { |line| stdin.puts(line.chomp) } stdin.close Chef::Log.debug("#{@new_resource} output: ") stdout_output = "" stdout.each_line { |line| stdout_output << line } stdout.close stderr_output = "" stderr.each_line { |line| stderr_output << line } stderr.close # fail if stderr contains anything if stderr_output.length > 0 raise Chef::Exceptions::ErlCall, stderr_output end # fail if the first 4 characters aren't "{ok," unless stdout_output[0..3].include?('{ok,') raise Chef::Exceptions::ErlCall, stdout_output end @new_resource.updated_by_last_action(true) Chef::Log.debug("#{@new_resource} #{stdout_output}") Chef::Log.info("#{@new_resouce} ran successfully") ensure Process.wait(pid) if pid end end end end end end chef-12.3.0/lib/chef/provider/group/0000755000004100000410000000000012520074675017203 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/group/gpasswd.rb0000644000004100000410000000347412520074675021210 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/group/groupadd' class Chef class Provider class Group class Gpasswd < Chef::Provider::Group::Groupadd def load_current_resource super end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/bin/gpasswd") } a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/gpasswd for #{@new_resource}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end end def set_members(members) unless members.empty? shell_out!("gpasswd -M #{members.join(',')} #{@new_resource.group_name}") else shell_out!("gpasswd -M \"\" #{@new_resource.group_name}") end end def add_member(member) shell_out!("gpasswd -a #{member} #{@new_resource.group_name}") end def remove_member(member) shell_out!("gpasswd -d #{member} #{@new_resource.group_name}") end end end end end chef-12.3.0/lib/chef/provider/group/pw.rb0000644000004100000410000001200412520074675020153 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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. # class Chef class Provider class Group class Pw < Chef::Provider::Group def load_current_resource super end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/sbin/pw") } a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/pw for #{@new_resource}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end end # Create the group def create_group command = "pw groupadd" command << set_options unless @new_resource.members.empty? # pw group[add|mod] -M is used to set the full membership list on a # new or existing group. Because pw groupadd does not support the -m # and -d options used by manage_group, we treat group creation as a # special case and use -M. Chef::Log.debug("#{@new_resource} setting group members: #{@new_resource.members.join(',')}") command += " -M #{@new_resource.members.join(',')}" end run_command(:command => command) end # Manage the group when it already exists def manage_group command = "pw groupmod" command << set_options member_options = set_members_options if member_options.empty? run_command(:command => command) else member_options.each do |option| run_command(:command => command + option) end end end # Remove the group def remove_group run_command(:command => "pw groupdel #{@new_resource.group_name}") end # Little bit of magic as per Adam's useradd provider to pull and assign the command line flags # # ==== Returns # :: A string containing the option and then the quoted value def set_options opts = " #{@new_resource.group_name}" if @new_resource.gid && (@current_resource.gid != @new_resource.gid) Chef::Log.debug("#{@new_resource}: current gid (#{@current_resource.gid}) doesnt match target gid (#{@new_resource.gid}), changing it") opts << " -g '#{@new_resource.gid}'" end opts end # Set the membership option depending on the current resource states def set_members_options opts = [ ] members_to_be_added = [ ] members_to_be_removed = [ ] if @new_resource.append # Append is set so we will only add members given in the # members list and remove members given in the # excluded_members list. if @new_resource.members && !@new_resource.members.empty? @new_resource.members.each do |member| members_to_be_added << member if !@current_resource.members.include?(member) end end if @new_resource.excluded_members && !@new_resource.excluded_members.empty? @new_resource.excluded_members.each do |member| members_to_be_removed << member if @current_resource.members.include?(member) end end else # Append is not set so we're resetting the membership of # the group to the given members. members_to_be_added = @new_resource.members @current_resource.members.each do |member| # No need to re-add a member if it's present in the new # list of members if members_to_be_added.include? member members_to_be_added.delete member else members_to_be_removed << member end end end unless members_to_be_added.empty? Chef::Log.debug("#{@new_resource} adding group members: #{members_to_be_added.join(',')}") opts << " -m #{members_to_be_added.join(',')}" end unless members_to_be_removed.empty? Chef::Log.debug("#{@new_resource} removing group members: #{members_to_be_removed.join(',')}") opts << " -d #{members_to_be_removed.join(',')}" end opts end end end end end chef-12.3.0/lib/chef/provider/group/usermod.rb0000644000004100000410000000625712520074675021220 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/group/groupadd' class Chef class Provider class Group class Usermod < Chef::Provider::Group::Groupadd provides :group, os: "openbsd" def load_current_resource super end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/sbin/usermod") } a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/usermod for #{@new_resource}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end requirements.assert(:modify, :manage) do |a| a.assertion { @new_resource.members.empty? || @new_resource.append } a.failure_message Chef::Exceptions::Group, "setting group members directly is not supported by #{self.to_s}, must set append true in group" # No whyrun alternative - this action is simply not supported. end requirements.assert(:all_actions) do |a| a.assertion { @new_resource.excluded_members.empty? } a.failure_message Chef::Exceptions::Group, "excluded_members is not supported by #{self.to_s}" # No whyrun alternative - this action is simply not supported. end end def set_members(members) return if members.empty? # This provider only supports adding members with # append. Only if the action is create we will go # ahead and add members. if @new_resource.action == :create members.each do |member| add_member(member) end else raise Chef::Exceptions::UnsupportedAction, "Setting members directly is not supported by #{self.to_s}" end end def add_member(member) shell_out!("usermod #{append_flags} #{@new_resource.group_name} #{member}") end def remove_member(member) # This provider only supports adding members with # append. This function should never be called. raise Chef::Exceptions::UnsupportedAction, "Removing members members is not supported by #{self.to_s}" end def append_flags case node[:platform] when "openbsd", "netbsd", "aix", "solaris2", "smartos", "omnios" "-G" when "solaris", "suse", "opensuse" "-a -G" end end end end end end chef-12.3.0/lib/chef/provider/group/groupmod.rb0000644000004100000410000001113512520074675021365 0ustar www-datawww-data# # Author:: Dan Crosta () # 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. # class Chef class Provider class Group class Groupmod < Chef::Provider::Group provides :group, os: "netbsd" def load_current_resource super [ "group", "user" ].each do |binary| raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/#{binary} for #{@new_resource}" unless ::File.exists?("/usr/sbin/#{binary}") end end # Create the group def create_group command = "group add" command << set_options shell_out!(command) add_group_members(@new_resource.members) end # Manage the group when it already exists def manage_group if @new_resource.append members_to_be_added = [ ] if @new_resource.excluded_members && !@new_resource.excluded_members.empty? # First find out if any member needs to be removed members_to_be_removed = [ ] @new_resource.excluded_members.each do |member| members_to_be_removed << member if @current_resource.members.include?(member) end unless members_to_be_removed.empty? # We are using a magic trick to remove the groups. reset_group_membership # Capture the members we need to add in # members_to_be_added to be added later on. @current_resource.members.each do |member| members_to_be_added << member unless members_to_be_removed.include?(member) end end end if @new_resource.members && !@new_resource.members.empty? @new_resource.members.each do |member| members_to_be_added << member if !@current_resource.members.include?(member) end end Chef::Log.debug("#{@new_resource} not changing group members, the group has no members to add") if members_to_be_added.empty? add_group_members(members_to_be_added) else # We are resetting the members of a group so use the same trick reset_group_membership Chef::Log.debug("#{@new_resource} setting group members to: none") if @new_resource.members.empty? add_group_members(@new_resource.members) end end # Remove the group def remove_group shell_out!("group del #{@new_resource.group_name}") end # Adds a list of usernames to the group using `user mod` def add_group_members(members) Chef::Log.debug("#{@new_resource} adding members #{members.join(', ')}") if !members.empty? members.each do |user| shell_out!("user mod -G #{@new_resource.group_name} #{user}") end end # This is tricky, but works: rename the existing group to # "_bak", create a new group with the same GID and # "", then set correct members on that group def reset_group_membership rename = "group mod -n #{@new_resource.group_name}_bak #{@new_resource.group_name}" shell_out!(rename) create = "group add" create << set_options(:overwrite_gid => true) shell_out!(create) remove = "group del #{@new_resource.group_name}_bak" shell_out!(remove) end # Little bit of magic as per Adam's useradd provider to pull and assign the command line flags # # ==== Returns # :: A string containing the option and then the quoted value def set_options(overwrite_gid=false) opts = "" if overwrite_gid || @new_resource.gid && (@current_resource.gid != @new_resource.gid) opts << " -g '#{@new_resource.gid}'" end if overwrite_gid opts << " -o" end opts << " #{@new_resource.group_name}" opts end end end end end chef-12.3.0/lib/chef/provider/group/dscl.rb0000644000004100000410000001506112520074675020460 0ustar www-datawww-data# # Author:: Dreamcat4 () # 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. # class Chef class Provider class Group class Dscl < Chef::Provider::Group provides :group, os: "darwin" def dscl(*args) host = "." stdout_result = ""; stderr_result = ""; cmd = "dscl #{host} -#{args.join(' ')}" status = shell_out(cmd) status.stdout.each_line { |line| stdout_result << line } status.stderr.each_line { |line| stderr_result << line } return [cmd, status, stdout_result, stderr_result] end def safe_dscl(*args) result = dscl(*args) return "" if ( args.first =~ /^delete/ ) && ( result[1].exitstatus != 0 ) raise(Chef::Exceptions::Group,"dscl error: #{result.inspect}") unless result[1].exitstatus == 0 raise(Chef::Exceptions::Group,"dscl error: #{result.inspect}") if result[2] =~ /No such key: / return result[2] end def load_current_resource @current_resource = Chef::Resource::Group.new(@new_resource.name) @current_resource.group_name(@new_resource.group_name) group_info = nil begin group_info = safe_dscl("read /Groups/#{@new_resource.group_name}") rescue Chef::Exceptions::Group @group_exists = false Chef::Log.debug("#{@new_resource} group does not exist") end if group_info group_info.each_line do |line| key, val = line.split(': ') val.strip! if val case key.downcase when 'primarygroupid' @new_resource.gid(val) unless @new_resource.gid @current_resource.gid(val) when 'groupmembership' @current_resource.members(val.split(' ')) end end end @current_resource end # get a free GID greater than 200 def get_free_gid(search_limit=1000) gid = nil; next_gid_guess = 200 groups_gids = safe_dscl("list /Groups gid") while(next_gid_guess < search_limit + 200) if groups_gids =~ Regexp.new("#{Regexp.escape(next_gid_guess.to_s)}\n") next_gid_guess += 1 else gid = next_gid_guess break end end return gid || raise("gid not found. Exhausted. Searched #{search_limit} times") end def gid_used?(gid) return false unless gid groups_gids = safe_dscl("list /Groups gid") !! ( groups_gids =~ Regexp.new("#{Regexp.escape(gid.to_s)}\n") ) end def set_gid @new_resource.gid(get_free_gid) if [nil,""].include? @new_resource.gid raise(Chef::Exceptions::Group,"gid is already in use") if gid_used?(@new_resource.gid) safe_dscl("create /Groups/#{@new_resource.group_name} PrimaryGroupID #{@new_resource.gid}") end def set_members # First reset the memberships if the append is not set unless @new_resource.append Chef::Log.debug("#{@new_resource} removing group members #{@current_resource.members.join(' ')}") unless @current_resource.members.empty? safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers ''") # clear guid list safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership ''") # clear user list @current_resource.members([ ]) end # Add any members that need to be added if @new_resource.members && !@new_resource.members.empty? members_to_be_added = [ ] @new_resource.members.each do |member| members_to_be_added << member if !@current_resource.members.include?(member) end unless members_to_be_added.empty? Chef::Log.debug("#{@new_resource} setting group members #{members_to_be_added.join(', ')}") safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{members_to_be_added.join(' ')}") end end # Remove any members that need to be removed if @new_resource.excluded_members && !@new_resource.excluded_members.empty? members_to_be_removed = [ ] @new_resource.excluded_members.each do |member| members_to_be_removed << member if @current_resource.members.include?(member) end unless members_to_be_removed.empty? Chef::Log.debug("#{@new_resource} removing group members #{members_to_be_removed.join(', ')}") safe_dscl("delete /Groups/#{@new_resource.group_name} GroupMembership #{members_to_be_removed.join(' ')}") end end end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/bin/dscl") } a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/bin/dscl for #{@new_resource.name}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end end def create_group dscl_create_group set_gid set_members end def manage_group if @new_resource.group_name && (@current_resource.group_name != @new_resource.group_name) dscl_create_group end if @new_resource.gid && (@current_resource.gid != @new_resource.gid) set_gid end if @new_resource.members || @new_resource.excluded_members set_members end end def dscl_create_group safe_dscl("create /Groups/#{@new_resource.group_name}") safe_dscl("create /Groups/#{@new_resource.group_name} Password '*'") end def remove_group safe_dscl("delete /Groups/#{@new_resource.group_name}") end end end end end chef-12.3.0/lib/chef/provider/group/aix.rb0000644000004100000410000000510212520074675020307 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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/group/groupadd' require 'chef/mixin/shell_out' class Chef class Provider class Group class Aix < Chef::Provider::Group::Groupadd def required_binaries [ "/usr/bin/mkgroup", "/usr/bin/chgroup", "/usr/bin/chgrpmem", "/usr/sbin/rmgroup" ] end def create_group command = "mkgroup" command << set_options << " #{@new_resource.group_name}" run_command(:command => command) modify_group_members end def manage_group command = "chgroup" options = set_options #Usage: chgroup [-R load_module] "attr=value" ... group if options.size > 0 command << options << " #{@new_resource.group_name}" run_command(:command => command) end modify_group_members end def remove_group run_command(:command => "rmgroup #{@new_resource.group_name}") end def add_member(member) shell_out!("chgrpmem -m + #{member} #{@new_resource.group_name}") end def set_members(members) return if members.empty? shell_out!("chgrpmem -m = #{members.join(',')} #{@new_resource.group_name}") end def remove_member(member) shell_out!("chgrpmem -m - #{member} #{@new_resource.group_name}") end def set_options opts = "" { :gid => "id" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option| if @current_resource.send(field) != @new_resource.send(field) if @new_resource.send(field) Chef::Log.debug("#{@new_resource} setting #{field.to_s} to #{@new_resource.send(field)}") opts << " '#{option}=#{@new_resource.send(field)}'" end end end opts end end end end end chef-12.3.0/lib/chef/provider/group/groupadd.rb0000644000004100000410000001140412520074675021335 0ustar www-datawww-data# # Author:: AJ Christensen () # 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. # class Chef class Provider class Group class Groupadd < Chef::Provider::Group def required_binaries [ "/usr/sbin/groupadd", "/usr/sbin/groupmod", "/usr/sbin/groupdel" ] end def load_current_resource super end def define_resource_requirements super required_binaries.each do |required_binary| requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?(required_binary) } a.failure_message Chef::Exceptions::Group, "Could not find binary #{required_binary} for #{@new_resource}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end end end # Create the group def create_group command = "groupadd" command << set_options command << groupadd_options run_command(:command => command) modify_group_members end # Manage the group when it already exists def manage_group command = "groupmod" command << set_options run_command(:command => command) modify_group_members end # Remove the group def remove_group run_command(:command => "groupdel #{@new_resource.group_name}") end def modify_group_members if @new_resource.append if @new_resource.members && !@new_resource.members.empty? members_to_be_added = [ ] @new_resource.members.each do |member| members_to_be_added << member if !@current_resource.members.include?(member) end members_to_be_added.each do |member| Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}") add_member(member) end end if @new_resource.excluded_members && !@new_resource.excluded_members.empty? members_to_be_removed = [ ] @new_resource.excluded_members.each do |member| members_to_be_removed << member if @current_resource.members.include?(member) end members_to_be_removed.each do |member| Chef::Log.debug("#{@new_resource} removing member #{member} from group #{@new_resource.group_name}") remove_member(member) end end else members_description = @new_resource.members.empty? ? "none" : @new_resource.members.join(", ") Chef::Log.debug("#{@new_resource} setting group members to: #{members_description}") set_members(@new_resource.members) end end def add_member(member) raise Chef::Exceptions::Group, "you must override add_member in #{self.to_s}" end def remove_member(member) raise Chef::Exceptions::Group, "you must override remove_member in #{self.to_s}" end def set_members(members) raise Chef::Exceptions::Group, "you must override set_members in #{self.to_s}" end # Little bit of magic as per Adam's useradd provider to pull the assign the command line flags # # ==== Returns # :: A string containing the option and then the quoted value def set_options opts = "" { :gid => "-g" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option| if @current_resource.send(field) != @new_resource.send(field) if @new_resource.send(field) opts << " #{option} '#{@new_resource.send(field)}'" Chef::Log.debug("#{@new_resource} set #{field.to_s} to #{@new_resource.send(field)}") end end end opts << " #{@new_resource.group_name}" end def groupadd_options opts = '' opts << " -r" if @new_resource.system opts << " -o" if @new_resource.non_unique opts end end end end end chef-12.3.0/lib/chef/provider/group/windows.rb0000644000004100000410000000617212520074675021230 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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/user' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/util/windows/net_group' end class Chef class Provider class Group class Windows < Chef::Provider::Group provides :group, os: "windows" def initialize(new_resource,run_context) super @net_group = Chef::Util::Windows::NetGroup.new(@new_resource.group_name) end def load_current_resource @current_resource = Chef::Resource::Group.new(@new_resource.name) @current_resource.group_name(@new_resource.group_name) members = nil begin members = @net_group.local_get_members rescue => e @group_exists = false Chef::Log.debug("#{@new_resource} group does not exist") end if members @current_resource.members(members) end @current_resource end def create_group @net_group.local_add manage_group end def manage_group if @new_resource.append members_to_be_added = [ ] @new_resource.members.each do |member| members_to_be_added << member if ! has_current_group_member?(member) end # local_add_members will raise ERROR_MEMBER_IN_ALIAS if a # member already exists in the group. @net_group.local_add_members(members_to_be_added) unless members_to_be_added.empty? members_to_be_removed = [ ] @new_resource.excluded_members.each do |member| member_sid = local_group_name_to_sid(member) members_to_be_removed << member if has_current_group_member?(member) end @net_group.local_delete_members(members_to_be_removed) unless members_to_be_removed.empty? else @net_group.local_set_members(@new_resource.members) end end def has_current_group_member?(member) member_sid = local_group_name_to_sid(member) @current_resource.members.include?(member_sid) end def remove_group @net_group.local_delete end def local_group_name_to_sid(group_name) locally_qualified_name = group_name.include?("\\") ? group_name : "#{ENV['COMPUTERNAME']}\\#{group_name}" Chef::ReservedNames::Win32::Security.lookup_account_name(locally_qualified_name)[1].to_s end end end end end chef-12.3.0/lib/chef/provider/group/suse.rb0000644000004100000410000000363312520074675020514 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/group/groupadd' class Chef class Provider class Group class Suse < Chef::Provider::Group::Groupadd def load_current_resource super end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/sbin/groupmod") } a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource.name}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end end def set_members(members) unless @current_resource.members.empty? shell_out!("groupmod -R #{@current_resource.members.join(',')} #{@new_resource.group_name}") end unless members.empty? shell_out!("groupmod -A #{members.join(',')} #{@new_resource.group_name}") end end def add_member(member) shell_out!("groupmod -A #{member} #{@new_resource.group_name}") end def remove_member(member) shell_out!("groupmod -R #{member} #{@new_resource.group_name}") end end end end end chef-12.3.0/lib/chef/provider/ifconfig/0000755000004100000410000000000012520074675017633 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/ifconfig/redhat.rb0000644000004100000410000000355312520074675021435 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/ifconfig' class Chef class Provider class Ifconfig class Redhat < Chef::Provider::Ifconfig def initialize(new_resource, run_context) super(new_resource, run_context) @config_template = %{ <% if @new_resource.device %>DEVICE=<%= @new_resource.device %><% end %> <% if @new_resource.onboot == "yes" %>ONBOOT=<%= @new_resource.onboot %><% end %> <% if @new_resource.bootproto %>BOOTPROTO=<%= @new_resource.bootproto %><% end %> <% if @new_resource.target %>IPADDR=<%= @new_resource.target %><% end %> <% if @new_resource.mask %>NETMASK=<%= @new_resource.mask %><% end %> <% if @new_resource.network %>NETWORK=<%= @new_resource.network %><% end %> <% if @new_resource.bcast %>BROADCAST=<%= @new_resource.bcast %><% end %> <% if @new_resource.onparent %>ONPARENT=<%= @new_resource.onparent %><% end %> <% if @new_resource.hwaddr %>HWADDR=<%= @new_resource.hwaddr %><% end %> <% if @new_resource.metric %>METRIC=<%= @new_resource.metric %><% end %> <% if @new_resource.mtu %>MTU=<%= @new_resource.mtu %><% end %> } @config_path = "/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}" end end end end end chef-12.3.0/lib/chef/provider/ifconfig/debian.rb0000644000004100000410000000624512520074675021411 0ustar www-datawww-data# # Author:: Xabier de Zuazo (xabier@onddo.com) # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/ifconfig' require 'chef/util/file_edit' class Chef class Provider class Ifconfig class Debian < Chef::Provider::Ifconfig INTERFACES_FILE = "/etc/network/interfaces" INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d" def initialize(new_resource, run_context) super(new_resource, run_context) @config_template = %{ <% if @new_resource.device %> <% if @new_resource.onboot == "yes" %>auto <%= @new_resource.device %><% end %> <% case @new_resource.bootproto when "dhcp" %> iface <%= @new_resource.device %> inet dhcp <% when "bootp" %> iface <%= @new_resource.device %> inet bootp <% else %> iface <%= @new_resource.device %> inet static <% if @new_resource.target %>address <%= @new_resource.target %><% end %> <% if @new_resource.mask %>netmask <%= @new_resource.mask %><% end %> <% if @new_resource.network %>network <%= @new_resource.network %><% end %> <% if @new_resource.bcast %>broadcast <%= @new_resource.bcast %><% end %> <% if @new_resource.metric %>metric <%= @new_resource.metric %><% end %> <% if @new_resource.hwaddr %>hwaddress <%= @new_resource.hwaddr %><% end %> <% if @new_resource.mtu %>mtu <%= @new_resource.mtu %><% end %> <% end %> <% end %> } @config_path = "#{INTERFACES_DOT_D_DIR}/ifcfg-#{@new_resource.device}" end def generate_config enforce_interfaces_dot_d_sanity super end protected def enforce_interfaces_dot_d_sanity # create /etc/network/interfaces.d via dir resource (to get reporting, etc) dir = Chef::Resource::Directory.new(INTERFACES_DOT_D_DIR, run_context) dir.run_action(:create) new_resource.updated_by_last_action(true) if dir.updated_by_last_action? # roll our own file_edit resource, this will not get reported until we have a file_edit resource interfaces_dot_d_for_regexp = INTERFACES_DOT_D_DIR.gsub(/\./, '\.') # escape dots for the regexp regexp = %r{^\s*source\s+#{interfaces_dot_d_for_regexp}/\*\s*$} unless ::File.exists?(INTERFACES_FILE) && regexp.match(IO.read(INTERFACES_FILE)) converge_by("modifying #{INTERFACES_FILE} to source #{INTERFACES_DOT_D_DIR}") do conf = Chef::Util::FileEdit.new(INTERFACES_FILE) conf.insert_line_if_no_match(regexp, "source #{INTERFACES_DOT_D_DIR}/*") conf.write_file end end end end end end end chef-12.3.0/lib/chef/provider/ifconfig/aix.rb0000644000004100000410000000672212520074675020750 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar (kaustubh@clogeny.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/ifconfig' class Chef class Provider class Ifconfig class Aix < Chef::Provider::Ifconfig def load_current_resource @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name) @interface_exists = false found_interface = false interface = {} @status = shell_out("ifconfig -a") @status.stdout.each_line do |line| if !found_interface if line =~ /^(\S+):\sflags=(\S+)/ # We have interface name, if this is the interface for @current_resource, load info else skip till next interface is found. if $1 == @new_resource.device # Found interface found_interface = true @interface_exists = true @current_resource.target(@new_resource.target) @current_resource.device($1) interface[:flags] = $2 @current_resource.metric($1) if line =~ /metric\s(\S+)/ end end else # parse interface related information, stop when next interface is found. if line =~ /^(\S+):\sflags=(\S+)/ # we are done parsing interface info and hit another one, so stop. found_interface = false break else if found_interface # read up interface info @current_resource.inet_addr($1) if line =~ /inet\s(\S+)\s/ @current_resource.bcast($1) if line =~ /broadcast\s(\S+)/ @current_resource.mask(hex_to_dec_netmask($1)) if line =~ /netmask\s(\S+)\s/ end end end end @current_resource end private def add_command # ifconfig changes are temporary, chdev persist across reboots. raise Chef::Exceptions::Ifconfig, "interface metric attribute cannot be set for :add action" if @new_resource.metric command = "chdev -l #{@new_resource.device} -a netaddr=#{@new_resource.name}" command << " -a netmask=#{@new_resource.mask}" if @new_resource.mask command << " -a mtu=#{@new_resource.mtu}" if @new_resource.mtu command end def delete_command # ifconfig changes are temporary, chdev persist across reboots. "chdev -l #{@new_resource.device} -a state=down" end def loopback_device "lo0" end def hex_to_dec_netmask(netmask) # example '0xffff0000' -> '255.255.0.0' dec = netmask[2..3].to_i(16).to_s(10) [4,6,8].each { |n| dec = dec + "." + netmask[n..n+1].to_i(16).to_s(10) } dec end end end end end chef-12.3.0/lib/chef/provider/whyrun_safe_ruby_block.rb0000644000004100000410000000214012520074675023136 0ustar www-datawww-data# # Author:: Phil Dibowitz () # Copyright:: Copyright (c) 2013 Facebook # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Provider class WhyrunSafeRubyBlock < Chef::Provider::RubyBlock provides :whyrun_safe_ruby_block def action_run @new_resource.block.call @new_resource.updated_by_last_action(true) @run_context.events.resource_update_applied(@new_resource, :create, "execute the whyrun_safe_ruby_block #{@new_resource.name}") Chef::Log.info("#{@new_resource} called") end end end end chef-12.3.0/lib/chef/provider/lwrp_base.rb0000644000004100000410000001404712520074675020360 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008-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/provider' class Chef class Provider # == Chef::Provider::LWRPBase # Base class from which LWRP providers inherit. class LWRPBase < Provider # Chef::Provider::LWRPBase::InlineResources # Implementation of inline resource convergence for LWRP providers. See # Provider::LWRPBase.use_inline_resources for a longer explanation. # # This code is restricted to a module so that it can be selectively # applied to providers on an opt-in basis. module InlineResources # Class methods for InlineResources. Overrides the `action` DSL method # with one that enables inline resource convergence. module ClassMethods # Defines an action method on the provider, using # recipe_eval_with_update_check to execute the given block. def action(name, &block) define_method("action_#{name}") do recipe_eval_with_update_check(&block) end end end # Executes the given block in a temporary run_context with its own # resource collection. After the block is executed, any resources # declared inside are converged, and if any are updated, the # new_resource will be marked updated. def recipe_eval_with_update_check(&block) saved_run_context = @run_context temp_run_context = @run_context.dup @run_context = temp_run_context @run_context.resource_collection = Chef::ResourceCollection.new return_value = instance_eval(&block) Chef::Runner.new(@run_context).converge return_value ensure @run_context = saved_run_context if temp_run_context.resource_collection.any? {|r| r.updated? } new_resource.updated_by_last_action(true) end end end extend Chef::Mixin::ConvertToClassName extend Chef::Mixin::FromFile include Chef::DSL::Recipe # These were previously provided by Chef::Mixin::RecipeDefinitionDSLCore. # They are not included by its replacment, Chef::DSL::Recipe, but # they may be used in existing LWRPs. include Chef::DSL::PlatformIntrospection include Chef::DSL::DataQuery def self.build_from_file(cookbook_name, filename, run_context) provider_class = nil provider_name = filename_to_qualified_string(cookbook_name, filename) class_name = convert_to_class_name(provider_name) if Chef::Provider.const_defined?(class_name, false) Chef::Log.info("#{class_name} light-weight provider is already initialized -- Skipping loading #{filename}!") Chef::Log.debug("Overriding already defined LWRPs is not supported anymore starting with Chef 12.") provider_class = Chef::Provider.const_get(class_name) else provider_class = Class.new(self) Chef::Provider.const_set(class_name, provider_class) provider_class.class_from_file(filename) Chef::Log.debug("Loaded contents of #{filename} into a provider named #{provider_name} defined in Chef::Provider::#{class_name}") end provider_class end # Enables inline evaluation of resources in provider actions. # # Without this option, any resources declared inside the LWRP are added # to the resource collection after the current position at the time the # action is executed. Because they are added to the primary resource # collection for the chef run, they can notify other resources outside # the LWRP, and potentially be notified by resources outside the LWRP # (but this is complicated by the fact that they don't exist until the # provider executes). In this mode, it is impossible to correctly set the # updated_by_last_action flag on the parent LWRP resource, since it # executes and returns before its component resources are run. # # With this option enabled, each action creates a temporary run_context # with its own resource collection, evaluates the action's code in that # context, and then converges the resources created. If any resources # were updated, then this provider's new_resource will be marked updated. # # In this mode, resources created within the LWRP cannot interact with # external resources via notifies, though notifications to other # resources within the LWRP will work. Delayed notifications are executed # at the conclusion of the provider's action, *not* at the end of the # main chef run. # # This mode of evaluation is experimental, but is believed to be a better # set of tradeoffs than the append-after mode, so it will likely become # the default in a future major release of Chef. # def self.use_inline_resources extend InlineResources::ClassMethods include InlineResources end # DSL for defining a provider's actions. def self.action(name, &block) define_method("action_#{name}") do instance_eval(&block) end end # no-op `load_current_resource`. Allows simple LWRP providers to work # without defining this method explicitly (silences # Chef::Exceptions::Override exception) def load_current_resource end end end end chef-12.3.0/lib/chef/provider/ohai.rb0000644000004100000410000000267012520074675017321 0ustar www-datawww-data# # Author:: Michael Leianrtas () # Copyright:: Copyright (c) 2010 Michael Leinartas # 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 'ohai' class Chef class Provider class Ohai < Chef::Provider def whyrun_supported? true end def load_current_resource true end def action_reload converge_by("re-run ohai and merge results into node attributes") do ohai = ::Ohai::System.new # If @new_resource.plugin is nil, ohai will reload all the plugins # Otherwise it will only reload the specified plugin # Note that any changes to plugins, or new plugins placed on # the path are picked up by ohai. ohai.all_plugins @new_resource.plugin node.automatic_attrs.merge! ohai.data Chef::Log.info("#{@new_resource} reloaded") end end end end end chef-12.3.0/lib/chef/provider/dsc_resource.rb0000644000004100000410000001112212520074675021051 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/cmdlet' require 'chef/util/dsc/local_configuration_manager' require 'chef/mixin/powershell_type_coercions' require 'chef/util/dsc/resource_store' class Chef class Provider class DscResource < Chef::Provider include Chef::Mixin::PowershellTypeCoercions provides :dsc_resource, os: "windows" def initialize(new_resource, run_context) super @new_resource = new_resource @module_name = new_resource.module_name end def action_run if ! test_resource converge_by(generate_description) do result = set_resource end end end def load_current_resource end def whyrun_supported? true end def define_resource_requirements requirements.assert(:run) do |a| a.assertion { supports_dsc_invoke_resource? } err = ["You must have Powershell version >= 5.0.10018.0 to use dsc_resource."] a.failure_message Chef::Exceptions::NoProviderAvailable, err a.whyrun err + ["Assuming a previous resource installs Powershell 5.0.10018.0 or higher."] a.block_action! end requirements.assert(:run) do |a| a.assertion { meta_configuration['RefreshMode'] == 'Disabled' } err = ["The LCM must have its RefreshMode set to Disabled. "] a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ') a.whyrun err + ["Assuming a previous resource sets the RefreshMode."] a.block_action! end end protected def local_configuration_manager @local_configuration_manager ||= Chef::Util::DSC::LocalConfigurationManager.new( node, nil ) end def resource_store Chef::Util::DSC::ResourceStore.instance end def supports_dsc_invoke_resource? run_context && Chef::Platform.supports_dsc_invoke_resource?(node) end def generate_description @converge_description end def dsc_resource_name new_resource.resource.to_s end def module_name @module_name ||= begin found = resource_store.find(dsc_resource_name) r = case found.length when 0 raise Chef::Exceptions::ResourceNotFound, "Could not find #{dsc_resource_name}. Check to make "\ "sure that it shows up when running Get-DscResource" when 1 if found[0]['Module'].nil? :none else found[0]['Module']['Name'] end else raise Chef::Exceptions::MultipleDscResourcesFound, found end end end def test_resource result = invoke_resource(:test) # We really want this information from the verbose stream, # however Invoke-DscResource is not correctly writing to that # stream and instead just dumping to stdout @converge_description = result.stdout result.return_value[0]["InDesiredState"] end def set_resource result = invoke_resource(:set) result.return_value end def invoke_resource(method, output_format=:object) properties = translate_type(@new_resource.properties) switches = "-Method #{method.to_s} -Name #{@new_resource.resource}"\ " -Property #{properties} -Verbose" if module_name != :none switches += " -Module #{module_name}" end cmdlet = Chef::Util::Powershell::Cmdlet.new( node, "Invoke-DscResource #{switches}", output_format ) cmdlet.run! end def meta_configuration cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object) result = cmdlet.run! result.return_value end end end end chef-12.3.0/lib/chef/provider/registry_key.rb0000644000004100000410000001331212520074675021114 0ustar www-datawww-data# # Author:: Prajakta Purohit () # Author:: Lamont Granquist () # # Copyright:: 2011, Opscode, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/config' require 'chef/log' require 'chef/resource/file' require 'chef/mixin/checksum' require 'chef/provider' require 'etc' require 'fileutils' require 'chef/scan_access_control' require 'chef/win32/registry' class Chef class Provider class RegistryKey < Chef::Provider include Chef::Mixin::Checksum def whyrun_supported? true end def running_on_windows! unless Chef::Platform.windows? raise Chef::Exceptions::Win32NotWindows, "Attempt to manipulate the windows registry on a non-windows node" end end def load_current_resource running_on_windows! @current_resource ||= Chef::Resource::RegistryKey.new(@new_resource.key, run_context) @current_resource.key(@new_resource.key) @current_resource.architecture(@new_resource.architecture) @current_resource.recursive(@new_resource.recursive) if registry.key_exists?(@new_resource.key) @current_resource.values(registry.get_values(@new_resource.key)) end values_to_hash(@current_resource.unscrubbed_values) @current_resource end def registry @registry ||= Chef::Win32::Registry.new(@run_context, @new_resource.architecture) end def values_to_hash(values) if values @name_hash = Hash[values.map { |val| [val[:name], val] }] else @name_hash = {} end end def define_resource_requirements requirements.assert(:create, :create_if_missing, :delete, :delete_key) do |a| a.assertion{ registry.hive_exists?(@new_resource.key) } a.failure_message(Chef::Exceptions::Win32RegHiveMissing, "Hive #{@new_resource.key.split("\\").shift} does not exist") end requirements.assert(:create) do |a| a.assertion{ registry.key_exists?(@new_resource.key) } a.whyrun("Key #{@new_resource.key} does not exist. Unless it would have been created before, attempt to modify its values would fail.") end requirements.assert(:create, :create_if_missing) do |a| #If keys missing in the path and recursive == false a.assertion{ !registry.keys_missing?(@current_resource.key) || @new_resource.recursive } a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "Intermediate keys missing but recursive is set to false") a.whyrun("Intermediate keys in #{@new_resource.key} go not exist. Unless they would have been created earlier, attempt to modify them would fail.") end requirements.assert(:delete_key) do |a| #If key to be deleted has subkeys but recurssive == false a.assertion{ !registry.key_exists?(@new_resource.key) || !registry.has_subkeys?(@new_resource.key) || @new_resource.recursive } a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "#{@new_resource.key} has subkeys but recursive is set to false.") a.whyrun("#{@current_resource.key} has subkeys, but recursive is set to false. attempt to delete would fails unless subkeys were deleted prior to this action.") end end def action_create unless registry.key_exists?(@current_resource.key) converge_by("create key #{@new_resource.key}") do registry.create_key(@new_resource.key, @new_resource.recursive) end end @new_resource.unscrubbed_values.each do |value| if @name_hash.has_key?(value[:name]) current_value = @name_hash[value[:name]] unless current_value[:type] == value[:type] && current_value[:data] == value[:data] converge_by("set value #{value}") do registry.set_value(@new_resource.key, value) end end else converge_by("set value #{value}") do registry.set_value(@new_resource.key, value) end end end end def action_create_if_missing unless registry.key_exists?(@new_resource.key) converge_by("create key #{@new_resource.key}") do registry.create_key(@new_resource.key, @new_resource.recursive) end end @new_resource.unscrubbed_values.each do |value| unless @name_hash.has_key?(value[:name]) converge_by("create value #{value}") do registry.set_value(@new_resource.key, value) end end end end def action_delete if registry.key_exists?(@new_resource.key) @new_resource.unscrubbed_values.each do |value| if @name_hash.has_key?(value[:name]) converge_by("delete value #{value}") do registry.delete_value(@new_resource.key, value) end end end end end def action_delete_key if registry.key_exists?(@new_resource.key) converge_by("delete key #{@new_resource.key}") do registry.delete_key(@new_resource.key, @new_resource.recursive) end end end end end end chef-12.3.0/lib/chef/provider/link.rb0000644000004100000410000001425612520074675017341 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/config' require 'chef/log' require 'chef/mixin/file_class' require 'chef/resource/link' require 'chef/provider' require 'chef/scan_access_control' require 'chef/util/path_helper' class Chef class Provider class Link < Chef::Provider provides :link include Chef::Mixin::EnforceOwnershipAndPermissions include Chef::Mixin::FileClass def negative_complement(big) if big > 1073741823 # Fixnum max big -= (2**32) # diminished radix wrap to negative end big end private :negative_complement def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::Link.new(@new_resource.name) @current_resource.target_file(@new_resource.target_file) if file_class.symlink?(@current_resource.target_file) @current_resource.link_type(:symbolic) @current_resource.to( canonicalize(file_class.readlink(@current_resource.target_file)) ) else @current_resource.link_type(:hard) if ::File.exists?(@current_resource.target_file) if ::File.exists?(@new_resource.to) && file_class.stat(@current_resource.target_file).ino == file_class.stat(@new_resource.to).ino @current_resource.to(canonicalize(@new_resource.to)) else @current_resource.to("") end end end ScanAccessControl.new(@new_resource, @current_resource).set_all! @current_resource end def define_resource_requirements requirements.assert(:delete) do |a| a.assertion do if @current_resource.to @current_resource.link_type == @new_resource.link_type and (@current_resource.link_type == :symbolic or @current_resource.to != '') else true end end a.failure_message Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a #{@new_resource.link_type.to_s} link." a.whyrun("Would assume the link at #{@new_resource.target_file} was previously created") end end def canonicalize(path) Chef::Platform.windows? ? path.gsub('/', '\\') : path end def action_create # current_resource is the symlink that currently exists # new_resource is the symlink we need to create # to - the location to link to # target_file - the name of the link if @current_resource.to != canonicalize(@new_resource.to) || @current_resource.link_type != @new_resource.link_type # Handle the case where the symlink already exists and is pointing at a valid to_file if @current_resource.to # On Windows, to fix a symlink already pointing at a directory we must first # ::Dir.unlink the symlink (not the directory), while if we have a symlink # pointing at file we must use ::File.unlink on the symlink. # However if the new symlink will point to a file and the current symlink is pointing at a # directory we want to throw an exception and calling ::File.unlink on the directory symlink # will throw the correct ones. if Chef::Platform.windows? && ::File.directory?(@new_resource.to) && ::File.directory?(@current_resource.target_file) converge_by("unlink existing windows symlink to dir at #{@new_resource.target_file}") do ::Dir.unlink(@new_resource.target_file) end else converge_by("unlink existing symlink to file at #{@new_resource.target_file}") do ::File.unlink(@new_resource.target_file) end end end if @new_resource.link_type == :symbolic converge_by("create symlink at #{@new_resource.target_file} to #{@new_resource.to}") do file_class.symlink(canonicalize(@new_resource.to),@new_resource.target_file) Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.target_file} -> #{@new_resource.to}") Chef::Log.info("#{@new_resource} created") end elsif @new_resource.link_type == :hard converge_by("create hard link at #{@new_resource.target_file} to #{@new_resource.to}") do file_class.link(@new_resource.to, @new_resource.target_file) Chef::Log.debug("#{@new_resource} created #{@new_resource.link_type} link from #{@new_resource.target_file} -> #{@new_resource.to}") Chef::Log.info("#{@new_resource} created") end end end if @new_resource.link_type == :symbolic if access_controls.requires_changes? converge_by(access_controls.describe_changes) do access_controls.set_all end end end end def action_delete if @current_resource.to # Exists converge_by("delete link at #{@new_resource.target_file}") do ::File.delete(@new_resource.target_file) Chef::Log.info("#{@new_resource} deleted") end end end # Implementation components *should not* follow symlinks when managing # access control (e.g., use lchmod instead of chmod) if the resource is a # symlink. def manage_symlink_access? @new_resource.link_type == :symbolic end end end end chef-12.3.0/lib/chef/provider/windows_script.rb0000644000004100000410000000457512520074675021465 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 the user wants to run the script 32 bit && we are on a 64bit windows system && we are running a 64bit ruby ==> fail if ( target_architecture == :i386 ) && node_windows_architecture(run_context.node) == :x86_64 && !is_i386_process_on_x86_64_windows? 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 chef-12.3.0/lib/chef/provider/template/0000755000004100000410000000000012520074675017662 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/template/content.rb0000644000004100000410000000353012520074675021662 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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/mixin/template' require 'chef/file_content_management/content_base' class Chef class Provider class Template class Content < Chef::FileContentManagement::ContentBase include Chef::Mixin::Template def template_location @template_file_cache_location ||= begin template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook) end end private def file_for_provider context = TemplateContext.new(@new_resource.variables) context[:node] = @run_context.node context[:template_finder] = template_finder context._extend_modules(@new_resource.helper_modules) output = context.render_template(template_location) tempfile = Tempfile.open("chef-rendered-template") tempfile.binmode tempfile.write(output) tempfile.close tempfile end def template_finder @template_finder ||= begin TemplateFinder.new(run_context, @new_resource.cookbook_name, @run_context.node) end end end end end end chef-12.3.0/lib/chef/provider/remote_file/0000755000004100000410000000000012520074675020341 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/remote_file/cache_control_data.rb0000644000004100000410000001314512520074675024466 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Jesse Campbell () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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 'stringio' require 'chef/file_cache' require 'chef/json_compat' require 'chef/digester' require 'chef/exceptions' class Chef class Provider class RemoteFile # == CacheControlData # Implements per-uri storage of cache control data for a remote resource # along with a sanity check checksum of the file in question. # Provider::RemoteFile protocol implementation classes can use this # information to avoid re-fetching files when the current copy is up to # date. The way this information is used is protocol-dependent. For HTTP, # this information is sent to the origin server via headers to make a # conditional GET request. # # == API # The general shape of the API is active-record-the-pattern-like. New # instances should be instantiated via # `CacheControlData.load_and_validate`, which will do a find-or-create # operation and then sanity check the data against the checksum of the # current copy of the file. If there is no data or the sanity check # fails, the `etag` and `mtime` attributes will be set to nil; otherwise # they are populated with the previously saved values. # # After fetching a file, the CacheControlData instance should be updated # with new etag, mtime and checksum values in whatever format is # preferred by the protocol used. Then call #save to save the data to disk. class CacheControlData def self.load_and_validate(uri, current_copy_checksum) ccdata = new(uri) ccdata.load ccdata.validate!(current_copy_checksum) ccdata end # Entity Tag of the resource. HTTP-specific. See also: # http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.2 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19 attr_accessor :etag # Last modified time of the remote resource. Different protocols will # use different types for this field (e.g., string representation of a # specific date format, integer, etc.) For HTTP-specific references, # see: # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.1 # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 attr_accessor :mtime # SHA2-256 Hash of the file as last fetched. attr_accessor :checksum # URI of the resource as a String. This is the "primary key" used for # storage and retrieval. attr_reader :uri def initialize(uri) uri = uri.dup uri.password = "XXXX" unless uri.userinfo.nil? @uri = uri.to_s end def load if previous_cc_data = load_data apply(previous_cc_data) self else false end end def validate!(current_copy_checksum) if current_copy_checksum.nil? or checksum != current_copy_checksum reset! false else true end end # Saves the data to disk using Chef::FileCache. The filename is a # sanitized version of the URI with a MD5 of the same URI appended (to # avoid collisions between different URIs having the same sanitized # form). def save Chef::FileCache.store("remote_file/#{sanitized_cache_file_basename}", json_data) end # :nodoc: # JSON representation of this object for storage. def json_data Chef::JSONCompat.to_json(hash_data) end private def hash_data as_hash = {} as_hash["etag"] = etag as_hash["mtime"] = mtime as_hash["checksum"] = checksum as_hash end def reset! @etag, @mtime = nil, nil end def apply(previous_cc_data) @etag = previous_cc_data["etag"] @mtime = previous_cc_data["mtime"] @checksum = previous_cc_data["checksum"] end def load_data Chef::JSONCompat.parse(load_json_data) rescue Chef::Exceptions::FileNotFound, Chef::Exceptions::JSON::ParseError false end def load_json_data Chef::FileCache.load("remote_file/#{sanitized_cache_file_basename}") end def sanitized_cache_file_basename # Scrub and truncate in accordance with the goals of keeping the name # human-readable but within the bounds of local file system # path length limits scrubbed_uri = uri.gsub(/\W/, '_')[0..63] uri_md5 = Chef::Digester.instance.generate_md5_checksum(StringIO.new(uri)) "#{scrubbed_uri}-#{uri_md5}.json" end end end end end chef-12.3.0/lib/chef/provider/remote_file/ftp.rb0000644000004100000410000001142612520074675021463 0ustar www-datawww-data# # Author:: Jesse Campbell () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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 'uri' require 'tempfile' require 'net/ftp' require 'chef/provider/remote_file' require 'chef/file_content_management/tempfile' class Chef class Provider class RemoteFile class FTP attr_reader :uri attr_reader :new_resource attr_reader :current_resource def initialize(uri, new_resource, current_resource) @uri = uri @new_resource = new_resource @current_resource = current_resource validate_typecode! validate_path! end def hostname @uri.host end def port @uri.port end def use_passive_mode? ! new_resource.ftp_active_mode end def typecode uri.typecode end def user if uri.userinfo URI.unescape(uri.user) else 'anonymous' end end def pass if uri.userinfo URI.unescape(uri.password) else nil end end def directories parse_path if @directories.nil? @directories end def filename parse_path if @filename.nil? @filename end def fetch with_connection do get end end def ftp @ftp ||= Net::FTP.new end private def with_proxy_env saved_socks_env = ENV['SOCKS_SERVER'] ENV['SOCKS_SERVER'] = proxy_uri(@uri).to_s yield ensure ENV['SOCKS_SERVER'] = saved_socks_env end def with_connection with_proxy_env do connect yield end ensure disconnect end def validate_typecode! # Only support ascii and binary types if typecode and /\A[ai]\z/ !~ typecode raise ArgumentError, "invalid typecode: #{typecode.inspect}" end end def validate_path! parse_path end def connect # The access sequence is defined by RFC 1738 ftp.connect(hostname, port) ftp.passive = use_passive_mode? ftp.login(user, pass) directories.each do |cwd| ftp.voidcmd("CWD #{cwd}") end end def disconnect ftp.close end # Fetches using Net::FTP, returns a Tempfile with the content def get tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile if typecode ftp.voidcmd("TYPE #{typecode.upcase}") end ftp.getbinaryfile(filename, tempfile.path) tempfile.close if tempfile tempfile end #adapted from buildr/lib/buildr/core/transports.rb via chef/rest/rest_client.rb def proxy_uri(uri) proxy = Chef::Config["ftp_proxy"] proxy = URI.parse(proxy) if String === proxy if Chef::Config["ftp_proxy_user"] proxy.user = Chef::Config["ftp_proxy_user"] end if Chef::Config["ftp_proxy_pass"] proxy.password = Chef::Config["ftp_proxy_pass"] end excludes = Chef::Config[:no_proxy].to_s.split(/\s*,\s*/).compact excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" } return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") } end def parse_path path = uri.path.sub(%r{\A/}, '%2F') # re-encode the beginning slash because uri library decodes it. directories = path.split(%r{/}, -1) directories.each {|d| d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } } unless filename = directories.pop raise ArgumentError, "no filename: #{path.inspect}" end if filename.length == 0 || filename.end_with?( "/" ) raise ArgumentError, "no filename: #{path.inspect}" end @directories, @filename = directories, filename end end end end end chef-12.3.0/lib/chef/provider/remote_file/local_file.rb0000644000004100000410000000327412520074675022765 0ustar www-datawww-data# # Author:: Jesse Campbell () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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 'uri' require 'tempfile' require 'chef/provider/remote_file' class Chef class Provider class RemoteFile class LocalFile attr_reader :uri attr_reader :new_resource def initialize(uri, new_resource, current_resource) @new_resource = new_resource @uri = uri end # CHEF-4472: Remove the leading slash from windows paths that we receive from a file:// URI def fix_windows_path(path) path.gsub(/^\/([a-zA-Z]:)/,'\1') end # Fetches the file at uri, returning a Tempfile-like File handle def fetch source_path = Chef::Platform.windows? ? fix_windows_path(uri.path) : uri.path tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile Chef::Log.debug("#{new_resource} staging #{source_path} to #{tempfile.path}") FileUtils.cp(source_path, tempfile.path) tempfile.close if tempfile tempfile end end end end end chef-12.3.0/lib/chef/provider/remote_file/fetcher.rb0000644000004100000410000000257512520074675022317 0ustar www-datawww-data# # Author:: Jesse Campbell () # Author:: Lamont Granquist () # 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. # class Chef class Provider class RemoteFile class Fetcher def self.for_resource(uri, new_resource, current_resource) case uri.scheme when "http", "https" Chef::Provider::RemoteFile::HTTP.new(uri, new_resource, current_resource) when "ftp" Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource) when "file" Chef::Provider::RemoteFile::LocalFile.new(uri, new_resource, current_resource) else raise ArgumentError, "Invalid uri, Only http(s), ftp, and file are currently supported" end end end end end end chef-12.3.0/lib/chef/provider/remote_file/http.rb0000644000004100000410000000734512520074675021656 0ustar www-datawww-data# # Author:: Jesse Campbell () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2013 Jesse Campbell # 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/http/simple' require 'chef/digester' require 'chef/provider/remote_file' require 'chef/provider/remote_file/cache_control_data' class Chef class Provider class RemoteFile class HTTP attr_reader :uri attr_reader :new_resource attr_reader :current_resource # Parse the uri into instance variables def initialize(uri, new_resource, current_resource) @uri = uri @new_resource = new_resource @current_resource = current_resource end def headers conditional_get_headers.merge(new_resource.headers) end def conditional_get_headers cache_control_headers = {} if last_modified = cache_control_data.mtime and want_mtime_cache_control? cache_control_headers["if-modified-since"] = last_modified end if etag = cache_control_data.etag and want_etag_cache_control? cache_control_headers["if-none-match"] = etag end Chef::Log.debug("Cache control headers: #{cache_control_headers.inspect}") cache_control_headers end def fetch http = Chef::HTTP::Simple.new(uri, http_client_opts) tempfile = http.streaming_request(uri, headers) if tempfile update_cache_control_data(tempfile, http.last_response) tempfile.close end tempfile end private def update_cache_control_data(tempfile, response) cache_control_data.checksum = Chef::Digester.checksum_for_file(tempfile.path) cache_control_data.mtime = last_modified_time_from(response) cache_control_data.etag = etag_from(response) cache_control_data.save end def cache_control_data @cache_control_data ||= CacheControlData.load_and_validate(uri, current_resource.checksum) end def want_mtime_cache_control? new_resource.use_last_modified end def want_etag_cache_control? new_resource.use_etag end def last_modified_time_from(response) response['last_modified'] || response['date'] end def etag_from(response) response['etag'] end def http_client_opts opts={} # CHEF-3140 # 1. If it's already compressed, trying to compress it more will # probably be counter-productive. # 2. Some servers are misconfigured so that you GET $URL/file.tgz but # they respond with content type of tar and content encoding of gzip, # which tricks Chef::REST into decompressing the response body. In this # case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz, # which is not what you wanted. if uri.to_s =~ /gz$/ Chef::Log.debug("turning gzip compression off due to filename ending in gz") opts[:disable_gzip] = true end opts end end end end end chef-12.3.0/lib/chef/provider/remote_file/content.rb0000644000004100000410000000515712520074675022350 0ustar www-datawww-data# # Author:: Jesse Campbell () # Author:: Lamont Granquist () # 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 'uri' require 'tempfile' require 'chef/file_content_management/content_base' class Chef class Provider class RemoteFile class Content < Chef::FileContentManagement::ContentBase private def file_for_provider Chef::Log.debug("#{@new_resource} checking for changes") if current_resource_matches_target_checksum? Chef::Log.debug("#{@new_resource} checksum matches target checksum (#{@new_resource.checksum}) - not updating") else sources = @new_resource.source raw_file = try_multiple_sources(sources) end raw_file end # Given an array of source uris, iterate through them until one does not fail def try_multiple_sources(sources) sources = sources.dup source = sources.shift begin uri = URI.parse(source) raw_file = grab_file_from_uri(uri) rescue SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPServerException, Net::HTTPFatalError, Net::FTPError => e Chef::Log.warn("#{@new_resource} cannot be downloaded from #{source}: #{e.to_s}") if source = sources.shift Chef::Log.info("#{@new_resource} trying to download from another mirror") retry else raise e end end raw_file end # Given a source uri, return a Tempfile, or a File that acts like a Tempfile (close! method) def grab_file_from_uri(uri) Chef::Provider::RemoteFile::Fetcher.for_resource(uri, @new_resource, @current_resource).fetch end def current_resource_matches_target_checksum? @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{Regexp.escape(@new_resource.checksum)}/ end end end end end chef-12.3.0/lib/chef/provider/deploy/0000755000004100000410000000000012520074675017343 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/deploy/revision.rb0000644000004100000410000000571212520074675021533 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tim Hinderliter () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2009 Daniel DeLeo # Copyright:: Copyright (c) 2010 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' require 'chef/provider/deploy' require 'chef/json_compat' class Chef class Provider class Deploy class Revision < Chef::Provider::Deploy provides :deploy_revision provides :deploy_branch def all_releases sorted_releases end def action_deploy validate_release_history! super end def cleanup! super known_releases = sorted_releases Dir["#{Chef::Util::PathHelper.escape_glob(new_resource.deploy_to)}/releases/*"].each do |release_dir| unless known_releases.include?(release_dir) converge_by("Remove unknown release in #{release_dir}") do FileUtils.rm_rf(release_dir) end end end end protected def release_created(release) sorted_releases {|r| r.delete(release); r << release } end def release_deleted(release) sorted_releases { |r| r.delete(release)} end def release_slug scm_provider.revision_slug end private def sorted_releases cache = load_cache if block_given? yield cache save_cache(cache) end cache end def validate_release_history! sorted_releases do |release_list| release_list.each do |path| release_list.delete(path) unless ::File.exist?(path) end end end def sorted_releases_from_filesystem Dir.glob(Chef::Util::PathHelper.escape_glob(new_resource.deploy_to) + "/releases/*").sort_by { |d| ::File.ctime(d) } end def load_cache begin Chef::JSONCompat.parse(Chef::FileCache.load("revision-deploys/#{new_resource.name}")) rescue Chef::Exceptions::FileNotFound sorted_releases_from_filesystem end end def save_cache(cache) Chef::FileCache.store("revision-deploys/#{new_resource.name}", Chef::JSONCompat.to_json(cache)) cache end end end end end chef-12.3.0/lib/chef/provider/deploy/timestamped.rb0000644000004100000410000000171412520074675022207 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Provider class Deploy class Timestamped < Chef::Provider::Deploy provides :timestamped_deploy provides :deploy protected def release_slug Time.now.utc.strftime("%Y%m%d%H%M%S") end end end end end chef-12.3.0/lib/chef/provider/env/0000755000004100000410000000000012520074675016637 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/env/windows.rb0000644000004100000410000000437012520074675020662 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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/mixin/windows_env_helper' class Chef class Provider class Env class Windows < Chef::Provider::Env include Chef::Mixin::WindowsEnvHelper provides :env, os: "windows" def create_env obj = env_obj(@new_resource.key_name) unless obj obj = WIN32OLE.connect("winmgmts://").get("Win32_Environment").spawninstance_ obj.name = @new_resource.key_name obj.username = "" end obj.variablevalue = @new_resource.value obj.put_ value = @new_resource.value value = expand_path(value) if @new_resource.key_name.upcase == 'PATH' ENV[@new_resource.key_name] = value broadcast_env_change end def delete_env obj = env_obj(@new_resource.key_name) if obj obj.delete_ broadcast_env_change end if ENV[@new_resource.key_name] ENV.delete(@new_resource.key_name) end end def env_value(key_name) obj = env_obj(key_name) return obj ? obj.variablevalue : ENV[key_name] end def env_obj(key_name) wmi = WmiLite::Wmi.new # Note that by design this query is case insensitive with regard to key_name environment_variables = wmi.query("select * from Win32_Environment where name = '#{key_name}'") if environment_variables && environment_variables.length > 0 environment_variables[0].wmi_ole_object end end end end end end chef-12.3.0/lib/chef/provider/user/0000755000004100000410000000000012520074675017025 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/user/pw.rb0000644000004100000410000000702512520074675020004 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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/provider/user' class Chef class Provider class User class Pw < Chef::Provider::User def load_current_resource super raise Chef::Exceptions::User, "Could not find binary /usr/sbin/pw for #{@new_resource}" unless ::File.exists?("/usr/sbin/pw") end def create_user command = "pw useradd" command << set_options run_command(:command => command) modify_password end def manage_user command = "pw usermod" command << set_options run_command(:command => command) modify_password end def remove_user command = "pw userdel #{@new_resource.username}" command << " -r" if @new_resource.supports[:manage_home] run_command(:command => command) end def check_lock case @current_resource.password when /^\*LOCKED\*/ @locked = true else @locked = false end @locked end def lock_user run_command(:command => "pw lock #{@new_resource.username}") end def unlock_user run_command(:command => "pw unlock #{@new_resource.username}") end def set_options opts = " #{@new_resource.username}" field_list = { 'comment' => "-c", 'home' => "-d", 'gid' => "-g", 'uid' => "-u", 'shell' => "-s" } field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option| field_symbol = field.to_sym if @current_resource.send(field_symbol) != @new_resource.send(field_symbol) if @new_resource.send(field_symbol) Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}") opts << " #{option} '#{@new_resource.send(field_symbol)}'" end end end if @new_resource.supports[:manage_home] Chef::Log.debug("#{@new_resource} is managing the users home directory") opts << " -m" end opts end def modify_password if (not @new_resource.password.nil?) && (@current_resource.password != @new_resource.password) Chef::Log.debug("#{new_resource} updating password") command = "pw usermod #{@new_resource.username} -H 0" status = popen4(command, :waitlast => true) do |pid, stdin, stdout, stderr| stdin.puts "#{@new_resource.password}" end unless status.exitstatus == 0 raise Chef::Exceptions::User, "pw failed - #{status.inspect}!" end else Chef::Log.debug("#{new_resource} no change needed to password") end end end end end end chef-12.3.0/lib/chef/provider/user/solaris.rb0000644000004100000410000000522612520074675021033 0ustar www-datawww-data# # Author:: Stephen Nelson-Smith () # Author:: Jon Ramsey () # 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/provider/user/useradd' class Chef class Provider class User class Solaris < Chef::Provider::User::Useradd UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]] attr_writer :password_file def initialize(new_resource, run_context) @password_file = "/etc/shadow" super end def create_user super manage_password end def manage_user manage_password super end private def manage_password if @current_resource.password != @new_resource.password && @new_resource.password Chef::Log.debug("#{@new_resource} setting password to #{@new_resource.password}") write_shadow_file end end def write_shadow_file buffer = Tempfile.new("shadow", "/etc") ::File.open(@password_file) do |shadow_file| shadow_file.each do |entry| user = entry.split(":").first if user == @new_resource.username buffer.write(updated_password(entry)) else buffer.write(entry) end end end buffer.close # FIXME: mostly duplicates code with file provider deploying a file mode = ::File.stat(@password_file).mode & 07777 uid = ::File.stat(@password_file).uid gid = ::File.stat(@password_file).gid FileUtils.chown uid, gid, buffer.path FileUtils.chmod mode, buffer.path FileUtils.mv buffer.path, @password_file end def updated_password(entry) fields = entry.split(":") fields[1] = @new_resource.password fields[2] = days_since_epoch fields.join(":") end def days_since_epoch (Time.now.to_i / 86400).floor end end end end end chef-12.3.0/lib/chef/provider/user/dscl.rb0000644000004100000410000006451012520074675020305 0ustar www-datawww-data# # Author:: Dreamcat4 () # 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 'mixlib/shellout' require 'chef/provider/user' require 'openssl' require 'plist' require 'chef/util/path_helper' class Chef class Provider class User # # The most tricky bit of this provider is the way it deals with user passwords. # Mac OS X has different password shadow calculations based on the version. # < 10.7 => password shadow calculation format SALTED-SHA1 # => stored in: /var/db/shadow/hash/#{guid} # => shadow binary length 68 bytes # => First 4 bytes salt / Next 64 bytes shadow value # = 10.7 => password shadow calculation format SALTED-SHA512 # => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist # => shadow binary length 68 bytes # => First 4 bytes salt / Next 64 bytes shadow value # > 10.7 => password shadow calculation format SALTED-SHA512-PBKDF2 # => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist # => shadow binary length 128 bytes # => Salt / Iterations are stored separately in the same file # # This provider only supports Mac OSX versions 10.7 and above class Dscl < Chef::Provider::User provides :user, os: "darwin" def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { mac_osx_version_less_than_10_7? == false } a.failure_message(Chef::Exceptions::User, "Chef::Provider::User::Dscl only supports Mac OS X versions 10.7 and above.") end requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/bin/dscl") } a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{@new_resource}!") end requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/usr/bin/plutil") } a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{@new_resource}!") end requirements.assert(:create, :modify, :manage) do |a| a.assertion do if @new_resource.password && mac_osx_version_greater_than_10_7? # SALTED-SHA512 password shadow hashes are not supported on 10.8 and above. !salted_sha512?(@new_resource.password) else true end end a.failure_message(Chef::Exceptions::User, "SALTED-SHA512 passwords are not supported on Mac 10.8 and above. \ If you want to set the user password using shadow info make sure you specify a SALTED-SHA512-PBKDF2 shadow hash \ in 'password', with the associated 'salt' and 'iterations'.") end requirements.assert(:create, :modify, :manage) do |a| a.assertion do if @new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(@new_resource.password) # salt and iterations should be specified when # SALTED-SHA512-PBKDF2 password shadow hash is given !@new_resource.salt.nil? && !@new_resource.iterations.nil? else true end end a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hash is given without associated \ 'salt' and 'iterations'. Please specify 'salt' and 'iterations' in order to set the user password using shadow hash.") end requirements.assert(:create, :modify, :manage) do |a| a.assertion do if @new_resource.password && !mac_osx_version_greater_than_10_7? # On 10.7 SALTED-SHA512-PBKDF2 is not supported !salted_sha512_pbkdf2?(@new_resource.password) else true end end a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hashes are not supported on \ Mac OS X version 10.7. Please specify a SALTED-SHA512 shadow hash in 'password' attribute to set the \ user password using shadow hash.") end end def load_current_resource @current_resource = Chef::Resource::User.new(@new_resource.username) @current_resource.username(@new_resource.username) @user_info = read_user_info if @user_info @current_resource.uid(dscl_get(@user_info, :uid)) @current_resource.gid(dscl_get(@user_info, :gid)) @current_resource.home(dscl_get(@user_info, :home)) @current_resource.shell(dscl_get(@user_info, :shell)) @current_resource.comment(dscl_get(@user_info, :comment)) @authentication_authority = dscl_get(@user_info, :auth_authority) if @new_resource.password && dscl_get(@user_info, :password) == "********" # A password is set. Let's get the password information from shadow file shadow_hash_binary = dscl_get(@user_info, :shadow_hash) # Calling shell_out directly since we want to give an input stream shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string) shadow_hash = Plist::parse_xml(shadow_hash_xml) if shadow_hash["SALTED-SHA512"] # Convert the shadow value from Base64 encoding to hex before consuming them @password_shadow_conversion_algorithm = "SALTED-SHA512" @current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first) elsif shadow_hash["SALTED-SHA512-PBKDF2"] @password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2" # Convert the entropy from Base64 encoding to hex before consuming them @current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first) @current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"]) # Convert the salt from Base64 encoding to hex before consuming them @current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first) else raise(Chef::Exceptions::User,"Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}") end end convert_group_name if @new_resource.gid else @user_exists = false Chef::Log.debug("#{@new_resource} user does not exist") end @current_resource end # # Provider Actions # def create_user dscl_create_user # set_password modifies the plist file of the user directly. So update # the password first before making any modifications to the user. set_password dscl_create_comment dscl_set_uid dscl_set_gid dscl_set_home dscl_set_shell end def manage_user # set_password modifies the plist file of the user directly. So update # the password first before making any modifications to the user. set_password if diverged_password? dscl_create_user if diverged?(:username) dscl_create_comment if diverged?(:comment) dscl_set_uid if diverged?(:uid) dscl_set_gid if diverged?(:gid) dscl_set_home if diverged?(:home) dscl_set_shell if diverged?(:shell) end # # Action Helpers # # # Create a user using dscl # def dscl_create_user run_dscl("create /Users/#{@new_resource.username}") end # # Saves the specified Chef user `comment` into RealName attribute # of Mac user. # def dscl_create_comment run_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'") end # # Sets the user id for the user using dscl. # If a `uid` is not specified, it finds the next available one starting # from 200 if `system` is set, 500 otherwise. # def dscl_set_uid @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '') if uid_used?(@new_resource.uid) raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use") end run_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}") end # # Find the next available uid on the system. starting with 200 if `system` is set, # 500 otherwise. # def get_free_uid(search_limit=1000) uid = nil base_uid = @new_resource.system ? 200 : 500 next_uid_guess = base_uid users_uids = run_dscl("list /Users uid") while(next_uid_guess < search_limit + base_uid) if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n") next_uid_guess += 1 else uid = next_uid_guess break end end return uid || raise("uid not found. Exhausted. Searched #{search_limit} times") end # # Returns true if uid is in use by a different account, false otherwise. # def uid_used?(uid) return false unless uid users_uids = run_dscl("list /Users uid").split("\n") uid_map = users_uids.inject({}) do |tmap, tuid| x = tuid.split tmap[x[1]] = x[0] tmap end if uid_map[uid.to_s] unless uid_map[uid.to_s] == @new_resource.username.to_s return true end end return false end # # Sets the group id for the user using dscl. Fails if a group doesn't # exist on the system with given group id. # def dscl_set_gid unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/) begin possible_gid = run_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last rescue Chef::Exceptions::DsclCommandFailed => e raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}") end @new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/) end run_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'") end # # Sets the home directory for the user. If `:manage_home` is set home # directory is managed (moved / created) for the user. # def dscl_set_home if @new_resource.home.nil? || @new_resource.home.empty? run_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory") return end if @new_resource.supports[:manage_home] validate_home_dir_specification! if (@current_resource.home == @new_resource.home) && !new_home_exists? ditto_home elsif !current_home_exists? && !new_home_exists? ditto_home elsif current_home_exists? move_home end end run_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'") end def validate_home_dir_specification! unless @new_resource.home =~ /^\// raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'") end end def current_home_exists? ::File.exist?("#{@current_resource.home}") end def new_home_exists? ::File.exist?("#{@new_resource.home}") end def ditto_home skel = "/System/Library/User Template/English.lproj" raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel) shell_out! "ditto '#{skel}' '#{@new_resource.home}'" ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home) end def move_home Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}") src = @current_resource.home FileUtils.mkdir_p(@new_resource.home) files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."] ::FileUtils.mv(files,@new_resource.home, :force => true) ::FileUtils.rmdir(src) ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home) end # # Sets the shell for the user using dscl. # def dscl_set_shell if @new_resource.shell || ::File.exists?("#{@new_resource.shell}") run_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'") else run_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'") end end # # Sets the password for the user based on given password parameters. # Chef supports specifying plain-text passwords and password shadow # hash data. # def set_password # Return if there is no password to set return if @new_resource.password.nil? shadow_info = prepare_password_shadow_info # Shadow info is saved as binary plist. Convert the info to binary plist. shadow_info_binary = StringIO.new command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -", :input => shadow_info.to_plist, :live_stream => shadow_info_binary) command.run_command if @user_info.nil? # User is just created. read_user_info() will read the fresh information # for the user with a cache flush. However with experimentation we've seen # that dscl cache is not immediately updated after the creation of the user # This is odd and needs to be investigated further. sleep 3 @user_info = read_user_info end # Replace the shadow info in user's plist dscl_set(@user_info, :shadow_hash, shadow_info_binary) save_user_info(@user_info) end # # Prepares the password shadow info based on the platform version. # def prepare_password_shadow_info shadow_info = { } entropy = nil salt = nil iterations = nil if mac_osx_version_10_7? hash_value = if salted_sha512?(@new_resource.password) @new_resource.password else # Create a random 4 byte salt salt = OpenSSL::Random.random_bytes(4) encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + @new_resource.password) hash_value = salt.unpack('H*').first + encoded_password end shadow_info["SALTED-SHA512"] = StringIO.new shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value) shadow_info else if salted_sha512_pbkdf2?(@new_resource.password) entropy = convert_to_binary(@new_resource.password) salt = convert_to_binary(@new_resource.salt) iterations = @new_resource.iterations else salt = OpenSSL::Random.random_bytes(32) iterations = @new_resource.iterations # Use the default if not specified by the user entropy = OpenSSL::PKCS5::pbkdf2_hmac( @new_resource.password, salt, iterations, 128, OpenSSL::Digest::SHA512.new ) end pbkdf_info = { } pbkdf_info["entropy"] = StringIO.new pbkdf_info["entropy"].string = entropy pbkdf_info["salt"] = StringIO.new pbkdf_info["salt"].string = salt pbkdf_info["iterations"] = iterations shadow_info["SALTED-SHA512-PBKDF2"] = pbkdf_info end shadow_info end # # Removes the user from the system after removing user from his groups # and deleting home directory if needed. # def remove_user if @new_resource.supports[:manage_home] # Remove home directory FileUtils.rm_rf(@current_resource.home) end # Remove the user from its groups run_dscl("list /Groups").each_line do |group| if member_of_group?(group.chomp) run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{@new_resource.username}'") end end # Remove user account run_dscl("delete /Users/#{@new_resource.username}") end # # Locks the user. # def lock_user run_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'") end # # Unlocks the user # def unlock_user auth_string = @authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip run_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'") end # # Returns true if the user is locked, false otherwise. # def locked? if @authentication_authority !!(@authentication_authority =~ /DisabledUser/ ) else false end end # # This is the interface base User provider requires to provide idempotency. # def check_lock return @locked = locked? end # # Helper functions # # # Returns true if the system state and desired state is different for # given attribute. # def diverged?(parameter) parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?) end def parameter_updated?(parameter) not (@new_resource.send(parameter) == @current_resource.send(parameter)) end # # We need a special check function for password since we support both # plain text and shadow hash data. # # Checks if password needs update based on platform version and the # type of the password specified. # def diverged_password? return false if @new_resource.password.nil? # Dscl provider supports both plain text passwords and shadow hashes. if mac_osx_version_10_7? if salted_sha512?(@new_resource.password) diverged?(:password) else !salted_sha512_password_match? end else # When a system is upgraded to a version 10.7+ shadow hashes of the users # will be updated when the user logs in. So it's possible that we will have # SALTED-SHA512 password in the current_resource. In that case we will force # password to be updated. return true if salted_sha512?(@current_resource.password) # Some system users don't have salts; this can happen if the system is # upgraded and the user hasn't logged in yet. In this case, we will force # the password to be updated. return true if @current_resource.salt.nil? if salted_sha512_pbkdf2?(@new_resource.password) diverged?(:password) || diverged?(:salt) || diverged?(:iterations) else !salted_sha512_pbkdf2_password_match? end end end # # Returns true if user is member of the specified group, false otherwise. # def member_of_group?(group_name) membership_info = "" begin membership_info = run_dscl("read /Groups/#{group_name}") rescue Chef::Exceptions::DsclCommandFailed # Raised if the group doesn't contain any members end # Output is something like: # GroupMembership: root admin etc members = membership_info.split(" ") members.shift # Get rid of GroupMembership: string members.include?(@new_resource.username) end # # DSCL Helper functions # # A simple map of Chef's terms to DSCL's terms. DSCL_PROPERTY_MAP = { :uid => "uid", :gid => "gid", :home => "home", :shell => "shell", :comment => "realname", :password => "passwd", :auth_authority => "authentication_authority", :shadow_hash => "ShadowHashData" }.freeze # Directory where the user plist files are stored for versions 10.7 and above USER_PLIST_DIRECTORY = "/var/db/dslocal/nodes/Default/users".freeze # # Reads the user plist and returns a hash keyed with DSCL properties specified # in DSCL_PROPERTY_MAP. Return nil if the user is not found. # def read_user_info user_info = nil # We flush the cache here in order to make sure that we read fresh information # for the user. shell_out("dscacheutil '-flushcache'") begin user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist" user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}") user_info = Plist::parse_xml(user_plist_info) rescue Chef::Exceptions::PlistUtilCommandFailed end user_info end # # Saves the given hash keyed with DSCL properties specified # in DSCL_PROPERTY_MAP to the disk. # def save_user_info(user_info) user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist" Plist::Emit.save_plist(user_info, user_plist_file) run_plutil("convert binary1 #{user_plist_file}") end # # Sets a value in user information hash using Chef attributes as keys. # def dscl_set(user_hash, key, value) raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key) user_hash[DSCL_PROPERTY_MAP[key]] = [ value ] user_hash end # # Gets a value from user information hash using Chef attributes as keys. # def dscl_get(user_hash, key) raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key) # DSCL values are set as arrays value = user_hash[DSCL_PROPERTY_MAP[key]] value.nil? ? value : value.first end # # System Helpets # def mac_osx_version # This provider will only be invoked on node[:platform] == "mac_os_x" # We do not check or assert that here. node[:platform_version] end def mac_osx_version_10_7? mac_osx_version.start_with?("10.7.") end def mac_osx_version_less_than_10_7? versions = mac_osx_version.split(".") # Make integer comparison in order not to report 10.10 less than 10.7 (versions[0].to_i <= 10 && versions[1].to_i < 7) end def mac_osx_version_greater_than_10_7? versions = mac_osx_version.split(".") # Make integer comparison in order not to report 10.10 less than 10.7 (versions[0].to_i >= 10 && versions[1].to_i > 7) end def run_dscl(*args) result = shell_out("dscl . -#{args.join(' ')}") return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 ) raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") unless result.exitstatus == 0 raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") if result.stdout =~ /No such key: / result.stdout end def run_plutil(*args) result = shell_out("plutil -#{args.join(' ')}") raise(Chef::Exceptions::PlistUtilCommandFailed,"plutil error: #{result.inspect}") unless result.exitstatus == 0 if result.stdout.encoding == Encoding::ASCII_8BIT result.stdout.encode("utf-8", "binary", :undef => :replace, :invalid => :replace, :replace => '?') else result.stdout end end def convert_binary_plist_to_xml(binary_plist_string) Mixlib::ShellOut.new("plutil -convert xml1 -o - -", :input => binary_plist_string).run_command.stdout end def convert_to_binary(string) string.unpack('a2'*(string.size/2)).collect { |i| i.hex.chr }.join end def salted_sha512?(string) !!(string =~ /^[[:xdigit:]]{136}$/) end def salted_sha512_password_match? # Salt is included in the first 4 bytes of shadow data salt = @current_resource.password.slice(0,8) shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + @new_resource.password) @current_resource.password == salt + shadow end def salted_sha512_pbkdf2?(string) !!(string =~ /^[[:xdigit:]]{256}$/) end def salted_sha512_pbkdf2_password_match? salt = convert_to_binary(@current_resource.salt) OpenSSL::PKCS5::pbkdf2_hmac( @new_resource.password, salt, @current_resource.iterations, 128, OpenSSL::Digest::SHA512.new ).unpack('H*').first == @current_resource.password end end end end end chef-12.3.0/lib/chef/provider/user/useradd.rb0000644000004100000410000001264212520074675021006 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'pathname' require 'chef/provider/user' class Chef class Provider class User class Useradd < Chef::Provider::User UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]] def create_user command = compile_command("useradd") do |useradd| useradd.concat(universal_options) useradd.concat(useradd_options) end shell_out!(*command) end def manage_user unless universal_options.empty? command = compile_command("usermod") do |u| u.concat(universal_options) end shell_out!(*command) end end def remove_user command = [ "userdel" ] command << "-r" if managing_home_dir? command << "-f" if new_resource.force command << new_resource.username shell_out!(*command) end def check_lock # we can get an exit code of 1 even when it's successful on # rhel/centos (redhat bug 578534). See additional error checks below. passwd_s = shell_out!("passwd", "-S", new_resource.username, :returns => [0,1]) if whyrun_mode? && passwd_s.stdout.empty? && passwd_s.stderr.match(/does not exist/) # if we're in whyrun mode and the user is not yet created we assume it would be return false end raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if passwd_s.stdout.empty? status_line = passwd_s.stdout.split(' ') case status_line[1] when /^P/ @locked = false when /^N/ @locked = false when /^L/ @locked = true end unless passwd_s.exitstatus == 0 raise_lock_error = false if ['redhat', 'centos'].include?(node[:platform]) passwd_version_check = shell_out!('rpm -q passwd') passwd_version = passwd_version_check.stdout.chomp unless passwd_version == 'passwd-0.73-1' raise_lock_error = true end else raise_lock_error = true end raise Chef::Exceptions::User, "Cannot determine if #{new_resource} is locked!" if raise_lock_error end @locked end def lock_user shell_out!("usermod", "-L", new_resource.username) end def unlock_user shell_out!("usermod", "-U", new_resource.username) end def compile_command(base_command) base_command = Array(base_command) yield base_command base_command << new_resource.username base_command end def universal_options @universal_options ||= begin opts = [] # magic allows UNIVERSAL_OPTIONS to be overridden in a subclass self.class::UNIVERSAL_OPTIONS.each do |field, option| update_options(field, option, opts) end if updating_home? if managing_home_dir? Chef::Log.debug("#{new_resource} managing the users home directory") opts << "-d" << new_resource.home << "-m" else Chef::Log.debug("#{new_resource} setting home to #{new_resource.home}") opts << "-d" << new_resource.home end end opts << "-o" if new_resource.non_unique || new_resource.supports[:non_unique] opts end end def update_options(field, option, opts) if @current_resource.send(field).to_s != new_resource.send(field).to_s if new_resource.send(field) Chef::Log.debug("#{new_resource} setting #{field} to #{new_resource.send(field)}") opts << option << new_resource.send(field).to_s end end end def useradd_options opts = [] opts << "-r" if new_resource.system opts end def updating_home? # will return false if paths are equivalent # Pathname#cleanpath does a better job than ::File::expand_path (on both unix and windows) # ::File.expand_path("///tmp") == ::File.expand_path("/tmp") => false # ::File.expand_path("\\tmp") => "C:/tmp" return true if @current_resource.home.nil? && new_resource.home new_resource.home and Pathname.new(@current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath end def managing_home_dir? new_resource.manage_home || new_resource.supports[:manage_home] end end end end end chef-12.3.0/lib/chef/provider/user/aix.rb0000644000004100000410000000631112520074675020134 0ustar www-datawww-data# # 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. class Chef class Provider class User class Aix < Chef::Provider::User::Useradd UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]] def create_user super add_password end def manage_user add_password manage_home super end # Aix does not support -r like other unix, sytem account is created by adding to 'system' group def useradd_options opts = [] opts << "-g" << "system" if new_resource.system opts end def check_lock lock_info = shell_out!("lsuser -a account_locked #{new_resource.username}") if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/) # if we're in whyrun mode and the user is not yet created we assume it would be return false end raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if lock_info.stdout.empty? status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout) if status && status[1] == "true" @locked = true else @locked = false end @locked end def lock_user shell_out!("chuser account_locked=true #{new_resource.username}") end def unlock_user shell_out!("chuser account_locked=false #{new_resource.username}") end private def add_password if @current_resource.password != @new_resource.password && @new_resource.password Chef::Log.debug("#{@new_resource.username} setting password to #{@new_resource.password}") command = "echo '#{@new_resource.username}:#{@new_resource.password}' | chpasswd -e" shell_out!(command) end end # Aix specific handling to update users home directory. def manage_home # -m option does not work on aix, so move dir. if updating_home? and managing_home_dir? universal_options.delete("-m") if ::File.directory?(@current_resource.home) Chef::Log.debug("Changing users home directory from #{@current_resource.home} to #{new_resource.home}") shell_out!("mv #{@current_resource.home} #{new_resource.home}") else Chef::Log.debug("Creating users home directory #{new_resource.home}") shell_out!("mkdir -p #{new_resource.home}") end end end end end end end chef-12.3.0/lib/chef/provider/user/windows.rb0000644000004100000410000000761212520074675021052 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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/user' require 'chef/exceptions' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/util/windows/net_user' end class Chef class Provider class User class Windows < Chef::Provider::User provides :user, os: "windows" def initialize(new_resource,run_context) super @net_user = Chef::Util::Windows::NetUser.new(@new_resource.username) end def load_current_resource @current_resource = Chef::Resource::User.new(@new_resource.name) @current_resource.username(@new_resource.username) user_info = nil begin user_info = @net_user.get_info @current_resource.uid(user_info[:user_id]) @current_resource.gid(user_info[:primary_group_id]) @current_resource.comment(user_info[:full_name]) @current_resource.home(user_info[:home_dir]) @current_resource.shell(user_info[:script_path]) rescue Chef::Exceptions::UserIDNotFound => e # e.message should be "The user name could not be found" but checking for that could cause a localization bug @user_exists = false Chef::Log.debug("#{@new_resource} does not exist (#{e.message})") end @current_resource end # Check to see if the user needs any changes # # === Returns # :: If a change is required # :: If the users are identical def compare_user unless @net_user.validate_credentials(@new_resource.password) Chef::Log.debug("#{@new_resource} password has changed") return true end [ :uid, :gid, :comment, :home, :shell ].any? do |user_attrib| !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib) end end def create_user @net_user.add(set_options) end def manage_user @net_user.update(set_options) end def remove_user @net_user.delete end def check_lock @net_user.check_enabled end def lock_user @net_user.disable_account end def unlock_user @net_user.enable_account end def set_options opts = {:name => @new_resource.username} field_list = { 'comment' => 'full_name', 'home' => 'home_dir', 'gid' => 'primary_group_id', 'uid' => 'user_id', 'shell' => 'script_path', 'password' => 'password' } field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option| field_symbol = field.to_sym if @current_resource.send(field_symbol) != @new_resource.send(field_symbol) if @new_resource.send(field_symbol) unless field_symbol == :password Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field_symbol)}") end opts[option.to_sym] = @new_resource.send(field_symbol) end end end opts end end end end end chef-12.3.0/lib/chef/provider/user.rb0000644000004100000410000001510212520074675017351 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/provider' require 'chef/mixin/command' require 'etc' class Chef class Provider class User < Chef::Provider include Chef::Mixin::Command attr_accessor :user_exists, :locked def initialize(new_resource, run_context) super @user_exists = true @locked = nil @shadow_lib_ok = true @group_name_resolved = true end def convert_group_name if @new_resource.gid.is_a? String @new_resource.gid(Etc.getgrnam(@new_resource.gid).gid) end rescue ArgumentError => e @group_name_resolved = false end def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::User.new(@new_resource.name) @current_resource.username(@new_resource.username) begin user_info = Etc.getpwnam(@new_resource.username) rescue ArgumentError => e @user_exists = false Chef::Log.debug("#{@new_resource} user does not exist") user_info = nil end if user_info @current_resource.uid(user_info.uid) @current_resource.gid(user_info.gid) @current_resource.home(user_info.dir) @current_resource.shell(user_info.shell) @current_resource.password(user_info.passwd) if @new_resource.comment user_info.gecos.force_encoding(@new_resource.comment.encoding) end @current_resource.comment(user_info.gecos) if @new_resource.password && @current_resource.password == 'x' begin require 'shadow' rescue LoadError @shadow_lib_ok = false else shadow_info = Shadow::Passwd.getspnam(@new_resource.username) @current_resource.password(shadow_info.sp_pwdp) end end convert_group_name if @new_resource.gid end @current_resource end def define_resource_requirements requirements.assert(:all_actions) do |a| a.assertion { @group_name_resolved } a.failure_message Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}" a.whyrun "group name #{@new_resource.gid} does not exist. This will cause group assignment to fail. Assuming this group will have been created previously." end requirements.assert(:all_actions) do |a| a.assertion { @shadow_lib_ok } a.failure_message Chef::Exceptions::MissingLibrary, "You must have ruby-shadow installed for password support!" a.whyrun "ruby-shadow is not installed. Attempts to set user password will cause failure. Assuming that this gem will have been previously installed." + "Note that user update converge may report false-positive on the basis of mismatched password. " end requirements.assert(:modify, :lock, :unlock) do |a| a.assertion { @user_exists } a.failure_message(Chef::Exceptions::User, "Cannot modify user #{@new_resource.username} - does not exist!") a.whyrun("Assuming user #{@new_resource.username} would have been created") end end # Check to see if the user needs any changes # # === Returns # :: If a change is required # :: If the users are identical def compare_user changed = [ :comment, :home, :shell, :password ].select do |user_attrib| !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib) end changed += [ :uid, :gid ].select do |user_attrib| !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib).to_s != @current_resource.send(user_attrib).to_s end changed.any? end def action_create if !@user_exists converge_by("create user #{@new_resource.username}") do create_user Chef::Log.info("#{@new_resource} created") end elsif compare_user converge_by("alter user #{@new_resource.username}") do manage_user Chef::Log.info("#{@new_resource} altered") end end end def action_remove if @user_exists converge_by("remove user #{@new_resource.username}") do remove_user Chef::Log.info("#{@new_resource} removed") end end end def remove_user raise NotImplementedError end def action_manage if @user_exists && compare_user converge_by("manage user #{@new_resource.username}") do manage_user Chef::Log.info("#{@new_resource} managed") end end end def manage_user raise NotImplementedError end def action_modify if compare_user converge_by("modify user #{@new_resource.username}") do manage_user Chef::Log.info("#{@new_resource} modified") end end end def action_lock if check_lock() == false converge_by("lock the user #{@new_resource.username}") do lock_user Chef::Log.info("#{@new_resource} locked") end else Chef::Log.debug("#{@new_resource} already locked - nothing to do") end end def check_lock raise NotImplementedError end def lock_user raise NotImplementedError end def action_unlock if check_lock() == true converge_by("unlock user #{@new_resource.username}") do unlock_user Chef::Log.info("#{@new_resource} unlocked") end else Chef::Log.debug("#{@new_resource} already unlocked - nothing to do") end end def unlock_user raise NotImplementedError end end end end chef-12.3.0/lib/chef/provider/file.rb0000644000004100000410000004465212520074675017326 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2008-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/config' require 'chef/log' require 'chef/resource/file' require 'chef/provider' require 'etc' require 'fileutils' require 'chef/scan_access_control' require 'chef/mixin/checksum' require 'chef/mixin/file_class' require 'chef/util/backup' require 'chef/util/diff' require 'chef/deprecation/provider/file' require 'chef/deprecation/warnings' require 'chef/file_content_management/deploy' # The Tao of File Providers: # - the content provider must always return a tempfile that we can delete/mv # - do_create_file shall always create the file first and obey umask when perms are not specified # - do_contents_changes may assume the destination file exists (simplifies exception checking, # and always gives us something to diff against) # - do_contents_changes must restore the perms to the dest file and not obliterate them with # random tempfile permissions # - do_acl_changes may assume perms were not modified between lcr and when it runs (although the # file may have been created) class Chef class Provider class File < Chef::Provider include Chef::Mixin::EnforceOwnershipAndPermissions include Chef::Mixin::Checksum include Chef::Util::Selinux include Chef::Mixin::FileClass extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::File add_deprecation_warnings_for(Chef::Deprecation::Provider::File.instance_methods) provides :file attr_reader :deployment_strategy attr_accessor :needs_creating attr_accessor :needs_unlinking attr_accessor :managing_symlink def initialize(new_resource, run_context) @content_class ||= Chef::Provider::File::Content if new_resource.respond_to?(:atomic_update) @deployment_strategy = Chef::FileContentManagement::Deploy.strategy(new_resource.atomic_update) end super end def whyrun_supported? true end def load_current_resource # true if there is a symlink and we need to manage what it points at @managing_symlink = file_class.symlink?(new_resource.path) && ( new_resource.manage_symlink_source || new_resource.manage_symlink_source.nil? ) # true if there is a non-file thing in the way that we need to unlink first @needs_unlinking = if ::File.exist?(new_resource.path) if managing_symlink? !symlink_to_real_file?(new_resource.path) else !real_file?(new_resource.path) end else false end # true if we are going to be creating a new file @needs_creating = !::File.exist?(new_resource.path) || needs_unlinking? # Let children resources override constructing the @current_resource @current_resource ||= Chef::Resource::File.new(new_resource.name) current_resource.path(new_resource.path) if !needs_creating? # we are updating an existing file if managing_content? Chef::Log.debug("#{new_resource} checksumming file at #{new_resource.path}.") current_resource.checksum(checksum(current_resource.path)) else # if the file does not exist or is not a file, then the checksum is invalid/pointless current_resource.checksum(nil) end load_resource_attributes_from_file(current_resource) end current_resource end def define_resource_requirements # deep inside FAC we have to assert requirements, so call FACs hook to set that up access_controls.define_resource_requirements # Make sure the parent directory exists, otherwise fail. For why-run assume it would have been created. requirements.assert(:create, :create_if_missing, :touch) do |a| parent_directory = ::File.dirname(@new_resource.path) a.assertion { ::File.directory?(parent_directory) } a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist.") a.whyrun("Assuming directory #{parent_directory} would have been created") end # Make sure the file is deletable if it exists, otherwise fail. if ::File.exist?(@new_resource.path) requirements.assert(:delete) do |a| a.assertion { ::File.writable?(@new_resource.path) } a.failure_message(Chef::Exceptions::InsufficientPermissions,"File #{@new_resource.path} exists but is not writable so it cannot be deleted") end end error, reason, whyrun_message = inspect_existing_fs_entry requirements.assert(:create) do |a| a.assertion { error.nil? } a.failure_message(error, reason) a.whyrun(whyrun_message) # Subsequent attempts to read the fs entry at the path (e.g., for # calculating checksums) could blow up, so give up trying to continue # why-running. a.block_action! end end def action_create do_generate_content do_validate_content do_unlink do_create_file do_contents_changes do_acl_changes do_selinux load_resource_attributes_from_file(@new_resource) end def action_create_if_missing unless ::File.exist?(@new_resource.path) action_create else Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.") end end def action_delete if ::File.exists?(@new_resource.path) converge_by("delete file #{@new_resource.path}") do do_backup unless file_class.symlink?(@new_resource.path) ::File.delete(@new_resource.path) Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}") end end end def action_touch action_create converge_by("update utime on file #{@new_resource.path}") do time = Time.now ::File.utime(time, time, @new_resource.path) Chef::Log.info("#{@new_resource} updated atime and mtime to #{time}") end end # Implementation components *should* follow symlinks when managing access # control (e.g., use chmod instead of lchmod even if the path we're # managing is a symlink). def manage_symlink_access? false end private # What to check in this resource to see if we're going to be actively managing # content (for things like doing checksums in load_current_resource). Expected to # be overridden in subclasses. def managing_content? return true if @new_resource.checksum return true if !@new_resource.content.nil? && @action != :create_if_missing false end # Handles resource requirements for action :create when some fs entry # already exists at the destination path. For actions other than create, # we don't care what kind of thing is at the destination path because: # * for :create_if_missing, we're assuming the user wanted to avoid blowing away the non-file here # * for :touch, we can modify perms of whatever is at this path, regardless of its type # * for :delete, we can blow away whatever is here, regardless of its type # # For the action :create case, we need to deal with user-selectable # behavior to see if we're in an error condition. # * If there's no file at the destination path currently, we're cool to # create it. # * If the fs entry that currently exists at the destination is a regular # file, we're cool to update it with new content. # * If the fs entry is a symlink AND the resource has # `manage_symlink_source` enabled, we need to verify that the symlink is # a valid pointer to a real file. If it is, we can manage content and # permissions on the symlink source, otherwise, error. # * If `manage_symlink_source` is not enabled, fall through. # * If force_unlink is true, action :create will unlink whatever is in the way. # * If force_unlink is false, we're in an exceptional situation, so we # want to error. # # Note that this method returns values to be used with requirement # assertions, which then decide whether or not to raise or issue a # warning for whyrun mode. def inspect_existing_fs_entry path = @new_resource.path if !l_exist?(path) [nil, nil, nil] elsif real_file?(path) [nil, nil, nil] elsif file_class.symlink?(path) && @new_resource.manage_symlink_source verify_symlink_sanity(path) elsif file_class.symlink?(@new_resource.path) && @new_resource.manage_symlink_source.nil? Chef::Log.warn("File #{path} managed by #{@new_resource} is really a symlink. Managing the source file instead.") Chef::Log.warn("Disable this warning by setting `manage_symlink_source true` on the resource") Chef::Log.warn("In a future Chef release, 'manage_symlink_source' will not be enabled by default") verify_symlink_sanity(path) elsif @new_resource.force_unlink [nil, nil, nil] else [ Chef::Exceptions::FileTypeMismatch, "File #{path} exists, but is a #{file_type_string(@new_resource.path)}, set force_unlink to true to remove", "Assuming #{file_type_string(@new_resource.path)} at #{@new_resource.path} would have been removed by a previous resource" ] end end # Returns values suitable for use in a requirements assertion statement # when managing symlink source. If we're managing symlink source we can # hit 3 error cases: # 1. Symlink to nowhere: File.realpath(symlink) -> raise Errno::ENOENT # 2. Symlink loop: File.realpath(symlink) -> raise Errno::ELOOP # 3. Symlink to not-a-real-file: File.realpath(symlink) -> (directory|blockdev|etc.) # If any of the above apply, returns a 3-tuple of Exception class, # exception message, whyrun message; otherwise returns a 3-tuple of nil. def verify_symlink_sanity(path) real_path = ::File.realpath(path) if real_file?(real_path) [nil, nil, nil] else [ Chef::Exceptions::FileTypeMismatch, "File #{path} exists, but is a symlink to #{real_path} which is a #{file_type_string(real_path)}. " + "Disable manage_symlink_source and set force_unlink to remove it.", "Assuming symlink #{path} or source file #{real_path} would have been fixed by a previous resource" ] end rescue Errno::ELOOP [ Chef::Exceptions::InvalidSymlink, "Symlink at #{path} (pointing to #{::File.readlink(path)}) exists but attempting to resolve it creates a loop", "Assuming symlink loop would be fixed by a previous resource" ] rescue Errno::ENOENT [ Chef::Exceptions::InvalidSymlink, "Symlink at #{path} (pointing to #{::File.readlink(path)}) exists but attempting to resolve it leads to a nonexistent file", "Assuming symlink source would be created by a previous resource" ] end def content @content ||= begin load_current_resource if @current_resource.nil? @content_class.new(@new_resource, @current_resource, @run_context) end end def file_type_string(path) case when ::File.blockdev?(path) "block device" when ::File.chardev?(path) "char device" when ::File.directory?(path) "directory" when ::File.pipe?(path) "pipe" when ::File.socket?(path) "socket" when file_class.symlink?(path) "symlink" else "unknown filetype" end end def real_file?(path) !file_class.symlink?(path) && ::File.file?(path) end # like real_file? that follows (sane) symlinks def symlink_to_real_file?(path) begin real_file?(::File.realpath(path)) rescue Errno::ELOOP, Errno::ENOENT false end end # Similar to File.exist?, but also returns true in the case that the # named file is a broken symlink. def l_exist?(path) ::File.exist?(path) || file_class.symlink?(path) end def unlink(path) # Directories can not be unlinked. Remove them using FileUtils. if ::File.directory?(path) FileUtils.rm_rf(path) else ::File.unlink(path) end end def do_generate_content # referencing the tempfile magically causes content to be generated tempfile end def tempfile_checksum @tempfile_checksum ||= checksum(tempfile.path) end def do_validate_content if new_resource.checksum && tempfile && ( new_resource.checksum != tempfile_checksum ) raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(tempfile_checksum)) end if tempfile new_resource.verify.each do |v| if ! v.verify(tempfile.path) raise Chef::Exceptions::ValidationFailed.new "Proposed content for #{new_resource.path} failed verification #{v}" end end end end def do_unlink if @new_resource.force_unlink if needs_unlinking? # unlink things that aren't normal files description = "unlink #{file_type_string(@new_resource.path)} at #{@new_resource.path}" converge_by(description) do unlink(@new_resource.path) end end end end def do_create_file if needs_creating? converge_by("create new file #{@new_resource.path}") do deployment_strategy.create(@new_resource.path) Chef::Log.info("#{@new_resource} created file #{@new_resource.path}") end end end def do_backup(file = nil) Chef::Util::Backup.new(@new_resource, file).backup! end def diff @diff ||= Chef::Util::Diff.new end def update_file_contents do_backup unless needs_creating? deployment_strategy.deploy(tempfile.path, ::File.realpath(@new_resource.path)) Chef::Log.info("#{@new_resource} updated file contents #{@new_resource.path}") if managing_content? @new_resource.checksum(checksum(@new_resource.path)) # for reporting end end def do_contents_changes # a nil tempfile is okay, means the resource has no content or no new content return if tempfile.nil? # but a tempfile that has no path or doesn't exist should not happen if tempfile.path.nil? || !::File.exists?(tempfile.path) raise "chef-client is confused, trying to deploy a file that has no path or does not exist..." end # the file? on the next line suppresses the case in why-run when we have a not-file here that would have otherwise been removed if ::File.file?(@new_resource.path) && contents_changed? description = [ "update content in file #{@new_resource.path} from \ #{short_cksum(@current_resource.checksum)} to #{short_cksum(tempfile_checksum)}" ] # Hide the diff output if the resource is marked as a sensitive resource if @new_resource.sensitive @new_resource.diff("suppressed sensitive resource") description << "suppressed sensitive resource" else diff.diff(@current_resource.path, tempfile.path) @new_resource.diff( diff.for_reporting ) unless needs_creating? description << diff.for_output end converge_by(description) do update_file_contents end end # unlink necessary to clean up in why-run mode tempfile.close tempfile.unlink end # This logic ideally will be made into some kind of generic # platform-dependent post-converge hook for file-like # resources, but for now we only have the single selinux use # case. def do_selinux(recursive = false) if resource_updated? && Chef::Config[:enable_selinux_file_permission_fixup] if selinux_enabled? converge_by("restore selinux security context") do restore_security_context(::File.realpath(@new_resource.path), recursive) end else Chef::Log.debug "selinux utilities can not be found. Skipping selinux permission fixup." end end end def do_acl_changes if access_controls.requires_changes? converge_by(access_controls.describe_changes) do access_controls.set_all end end end def contents_changed? Chef::Log.debug "calculating checksum of #{tempfile.path} to compare with #{@current_resource.checksum}" tempfile_checksum != @current_resource.checksum end def tempfile @tempfile ||= content.tempfile end def short_cksum(checksum) return "none" if checksum.nil? checksum.slice(0,6) end def load_resource_attributes_from_file(resource) if Chef::Platform.windows? # This is a work around for CHEF-3554. # OC-6534: is tracking the real fix for this workaround. # Add support for Windows equivalent, or implicit resource # reporting won't work for Windows. return end acl_scanner = ScanAccessControl.new(@new_resource, resource) acl_scanner.set_all! end def managing_symlink? !!@managing_symlink end def needs_creating? !!@needs_creating end def needs_unlinking? !!@needs_unlinking end end end end chef-12.3.0/lib/chef/provider/mdadm.rb0000644000004100000410000000706012520074675017461 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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/log' require 'chef/provider' class Chef class Provider class Mdadm < Chef::Provider provides :mdadm def popen4 raise Exception, "deprecated" end def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::Mdadm.new(@new_resource.name) @current_resource.raid_device(@new_resource.raid_device) Chef::Log.debug("#{@new_resource} checking for software raid device #{@current_resource.raid_device}") device_not_found = 4 mdadm = shell_out!("mdadm --detail --test #{@new_resource.raid_device}", :returns => [0,device_not_found]) exists = (mdadm.status == 0) @current_resource.exists(exists) end def action_create unless @current_resource.exists converge_by("create RAID device #{new_resource.raid_device}") do command = "yes | mdadm --create #{@new_resource.raid_device} --level #{@new_resource.level}" command << " --chunk=#{@new_resource.chunk}" unless @new_resource.level == 1 command << " --metadata=#{@new_resource.metadata}" command << " --bitmap=#{@new_resource.bitmap}" if @new_resource.bitmap command << " --raid-devices #{@new_resource.devices.length} #{@new_resource.devices.join(" ")}" Chef::Log.debug("#{@new_resource} mdadm command: #{command}") shell_out!(command) Chef::Log.info("#{@new_resource} created raid device (#{@new_resource.raid_device})") end else Chef::Log.debug("#{@new_resource} raid device already exists, skipping create (#{@new_resource.raid_device})") end end def action_assemble unless @current_resource.exists converge_by("assemble RAID device #{new_resource.raid_device}") do command = "yes | mdadm --assemble #{@new_resource.raid_device} #{@new_resource.devices.join(" ")}" Chef::Log.debug("#{@new_resource} mdadm command: #{command}") shell_out!(command) Chef::Log.info("#{@new_resource} assembled raid device (#{@new_resource.raid_device})") end else Chef::Log.debug("#{@new_resource} raid device already exists, skipping assemble (#{@new_resource.raid_device})") end end def action_stop if @current_resource.exists converge_by("stop RAID device #{new_resource.raid_device}") do command = "yes | mdadm --stop #{@new_resource.raid_device}" Chef::Log.debug("#{@new_resource} mdadm command: #{command}") shell_out!(command) Chef::Log.info("#{@new_resource} stopped raid device (#{@new_resource.raid_device})") end else Chef::Log.debug("#{@new_resource} raid device doesn't exist (#{@new_resource.raid_device}) - not stopping") end end end end end chef-12.3.0/lib/chef/provider/powershell_script.rb0000644000004100000410000000746212520074675022155 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 PowershellScript < Chef::Provider::WindowsScript provides :powershell_script, os: "windows" protected EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE ) {exit $LASTEXITCODE} else { exit 1 }}".freeze EXIT_STATUS_RESET_SCRIPT = "\n$global:LASTEXITCODE=$null".freeze # Process exit codes are strange with PowerShell. Unless you # explicitly call exit in Powershell, the powershell.exe # interpreter returns only 0 for success or 1 for failure. Since # we'd like to get specific exit codes from executable tools run # with Powershell, we do some work using the automatic variables # $? and $LASTEXITCODE to return the process exit code of the # last process run in the script if it is the last command # executed, otherwise 0 or 1 based on whether $? is set to true # (success, where we return 0) or false (where we return 1). def normalize_script_exit_status( code ) target_code = ( EXIT_STATUS_EXCEPTION_HANDLER + EXIT_STATUS_RESET_SCRIPT + "\n" + code.to_s + EXIT_STATUS_NORMALIZATION_SCRIPT ) convert_boolean_return = @new_resource.convert_boolean_return self.code = <) # 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 'tempfile' require 'chef/provider/execute' require 'forwardable' class Chef class Provider class Script < Chef::Provider::Execute extend Forwardable provides :bash provides :csh provides :perl provides :python provides :ruby provides :script def_delegators :@new_resource, :interpreter, :flags attr_accessor :code def initialize(new_resource, run_context) super self.code = new_resource.code end def command "\"#{interpreter}\" #{flags} \"#{script_file.path}\"" end def load_current_resource super # @todo Chef-13: change this to an exception if code.nil? Chef::Log.warn "#{@new_resource}: No code attribute was given, resource does nothing, this behavior is deprecated and will be removed in Chef-13" end end def action_run script_file.puts(code) script_file.close set_owner_and_group super unlink_script_file end def set_owner_and_group # FileUtils itself implements a no-op if +user+ or +group+ are nil # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file') # as an unprivileged user. FileUtils.chown(new_resource.user, new_resource.group, script_file.path) end def script_file @script_file ||= Tempfile.open("chef-script") end def unlink_script_file script_file && script_file.close! end end end end chef-12.3.0/lib/chef/provider/cron.rb0000644000004100000410000002103012520074675017331 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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/log' require 'chef/mixin/command' require 'chef/provider' class Chef class Provider class Cron < Chef::Provider include Chef::Mixin::Command provides :cron, os: ["!aix", "!solaris2"] SPECIAL_TIME_VALUES = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly] CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :time, :command, :mailto, :path, :shell, :home, :environment] WEEKDAY_SYMBOLS = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday] CRON_PATTERN = /\A([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+|[a-zA-Z]{3})\s([-0-9*,\/]+|[a-zA-Z]{3})\s(.*)/ SPECIAL_PATTERN = /\A(@(#{SPECIAL_TIME_VALUES.join('|')}))\s(.*)/ ENV_PATTERN = /\A(\S+)=(\S*)/ def initialize(new_resource, run_context) super(new_resource, run_context) @cron_exists = false @cron_empty = false end attr_accessor :cron_exists, :cron_empty def whyrun_supported? true end def load_current_resource crontab_lines = [] @current_resource = Chef::Resource::Cron.new(@new_resource.name) @current_resource.user(@new_resource.user) @cron_exists = false if crontab = read_crontab cron_found = false crontab.each_line do |line| case line.chomp when "# Chef Name: #{@new_resource.name}" Chef::Log.debug("Found cron '#{@new_resource.name}'") cron_found = true @cron_exists = true next when ENV_PATTERN set_environment_var($1, $2) if cron_found next when SPECIAL_PATTERN if cron_found @current_resource.time($2.to_sym) @current_resource.command($3) cron_found=false end when CRON_PATTERN if cron_found @current_resource.minute($1) @current_resource.hour($2) @current_resource.day($3) @current_resource.month($4) @current_resource.weekday($5) @current_resource.command($6) cron_found=false end next else cron_found=false # We've got a Chef comment with no following crontab line next end end Chef::Log.debug("Cron '#{@new_resource.name}' not found") unless @cron_exists else Chef::Log.debug("Cron empty for '#{@new_resource.user}'") @cron_empty = true end @current_resource end def cron_different? CRON_ATTRIBUTES.any? do |cron_var| @new_resource.send(cron_var) != @current_resource.send(cron_var) end end def action_create crontab = String.new newcron = String.new cron_found = false newcron = get_crontab_entry if @cron_exists unless cron_different? Chef::Log.debug("Skipping existing cron entry '#{@new_resource.name}'") return end read_crontab.each_line do |line| case line.chomp when "# Chef Name: #{@new_resource.name}" cron_found = true next when ENV_PATTERN crontab << line unless cron_found next when SPECIAL_PATTERN if cron_found cron_found = false crontab << newcron next end when CRON_PATTERN if cron_found cron_found = false crontab << newcron next end else if cron_found # We've got a Chef comment with no following crontab line crontab << newcron cron_found = false end end crontab << line end # Handle edge case where the Chef comment is the last line in the current crontab crontab << newcron if cron_found converge_by("update crontab entry for #{@new_resource}") do write_crontab crontab Chef::Log.info("#{@new_resource} updated crontab entry") end else crontab = read_crontab unless @cron_empty crontab << newcron converge_by("add crontab entry for #{@new_resource}") do write_crontab crontab Chef::Log.info("#{@new_resource} added crontab entry") end end end def action_delete if @cron_exists crontab = String.new cron_found = false read_crontab.each_line do |line| case line.chomp when "# Chef Name: #{@new_resource.name}" cron_found = true next when ENV_PATTERN next if cron_found when SPECIAL_PATTERN if cron_found cron_found = false next end when CRON_PATTERN if cron_found cron_found = false next end else # We've got a Chef comment with no following crontab line cron_found = false end crontab << line end description = cron_found ? "remove #{@new_resource.name} from crontab" : "save unmodified crontab" converge_by(description) do write_crontab crontab Chef::Log.info("#{@new_resource} deleted crontab entry") end end end private def set_environment_var(attr_name, attr_value) if %w(MAILTO PATH SHELL HOME).include?(attr_name) @current_resource.send(attr_name.downcase.to_sym, attr_value) else @current_resource.environment(@current_resource.environment.merge(attr_name => attr_value)) end end def read_crontab crontab = nil status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr| crontab = stdout.read end if status.exitstatus > 1 raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}" end crontab end def write_crontab(crontab) write_exception = false status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr| begin stdin.write crontab rescue Errno::EPIPE => e # popen4 could yield while child has already died. write_exception = true Chef::Log.debug("#{e.message}") end end if status.exitstatus > 0 || write_exception raise Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: #{status.exitstatus}" end end def get_crontab_entry newcron = "" newcron << "# Chef Name: #{new_resource.name}\n" [ :mailto, :path, :shell, :home ].each do |v| newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v) end @new_resource.environment.each do |name, value| newcron << "#{name}=#{value}\n" end if @new_resource.time newcron << "@#{@new_resource.time} #{@new_resource.command}\n" else newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n" end newcron end def weekday_in_crontab weekday_in_crontab = WEEKDAY_SYMBOLS.index(@new_resource.weekday) if weekday_in_crontab.nil? @new_resource.weekday else weekday_in_crontab.to_s end end end end end chef-12.3.0/lib/chef/provider/log.rb0000644000004100000410000000261112520074675017155 0ustar www-datawww-data# # Author:: Cary Penniman () # 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. # class Chef class Provider class Log # Chef log provider, allows logging to chef's logs from recipes class ChefLog < Chef::Provider provides :log def whyrun_supported? true end # No concept of a 'current' resource for logs, this is a no-op # # === Return # true:: Always return true def load_current_resource true end # Write the log to Chef's log # # === Return # true:: Always return true def action_write Chef::Log.send(@new_resource.level, @new_resource.message) @new_resource.updated_by_last_action(true) end end end end end chef-12.3.0/lib/chef/provider/mount/0000755000004100000410000000000012520074675017211 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/mount/mount.rb0000644000004100000410000002333712520074675020710 0ustar www-datawww-data# # Author:: Joshua Timberman () # 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/provider/mount' require 'chef/log' class Chef class Provider class Mount class Mount < Chef::Provider::Mount def initialize(new_resource, run_context) super @real_device = nil end attr_accessor :real_device def load_current_resource @current_resource = Chef::Resource::Mount.new(@new_resource.name) @current_resource.mount_point(@new_resource.mount_point) @current_resource.device(@new_resource.device) mounted? enabled? end def mountable? # only check for existence of non-remote devices if (device_should_exist? && !::File.exists?(device_real) ) raise Chef::Exceptions::Mount, "Device #{@new_resource.device} does not exist" elsif( @new_resource.mount_point != "none" && !::File.exists?(@new_resource.mount_point) ) raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist" end return true end def enabled? # Check to see if there is a entry in /etc/fstab. Last entry for a volume wins. enabled = false ::File.foreach("/etc/fstab") do |line| case line when /^[#\s]/ next when /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ enabled = true @current_resource.fstype($1) @current_resource.options($2) @current_resource.dump($3.to_i) @current_resource.pass($4.to_i) Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab") next when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/ enabled = false Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab") end end @current_resource.enabled(enabled) end def mounted? mounted = false # "mount" outputs the mount points as real paths. Convert # the mount_point of the resource to a real path in case it # contains symlinks in its parents dirs. real_mount_point = if ::File.exists? @new_resource.mount_point ::File.realpath(@new_resource.mount_point) else @new_resource.mount_point end shell_out!("mount").stdout.each_line do |line| case line when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(real_mount_point)}\s/ mounted = true Chef::Log.debug("Special device #{device_logstring} mounted as #{real_mount_point}") when /^([\/\w])+\son\s#{Regexp.escape(real_mount_point)}\s+/ mounted = false Chef::Log.debug("Special device #{$~[1]} mounted as #{real_mount_point}") end end @current_resource.mounted(mounted) end def mount_fs unless @current_resource.mounted mountable? command = "mount -t #{@new_resource.fstype}" command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty? command << case @new_resource.device_type when :device " #{device_real}" when :label " -L #{@new_resource.device}" when :uuid " -U #{@new_resource.device}" end command << " #{@new_resource.mount_point}" shell_out!(command) Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}") else Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}") end end def umount_fs if @current_resource.mounted shell_out!("umount #{@new_resource.mount_point}") Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}") else Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}") end end def remount_command return "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.mount_point}" end def remount_fs if @current_resource.mounted and @new_resource.supports[:remount] shell_out!(remount_command) @new_resource.updated_by_last_action(true) Chef::Log.debug("#{@new_resource} is remounted at #{@new_resource.mount_point}") elsif @current_resource.mounted umount_fs sleep 1 mount_fs else Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do") end end def enable_fs if @current_resource.enabled && mount_options_unchanged? Chef::Log.debug("#{@new_resource} is already enabled - nothing to do") return nil end if @current_resource.enabled # The current options don't match what we have, so # disable, then enable. disable_fs end ::File.open("/etc/fstab", "a") do |fstab| fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}") Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}") end end def disable_fs if @current_resource.enabled contents = [] found = false ::File.readlines("/etc/fstab").reverse_each do |line| if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s/ found = true Chef::Log.debug("#{@new_resource} is removed from fstab") next else contents << line end end ::File.open("/etc/fstab", "w") do |fstab| contents.reverse_each { |line| fstab.puts line} end else Chef::Log.debug("#{@new_resource} is not enabled - nothing to do") end end def network_device? @new_resource.device =~ /:/ || @new_resource.device =~ /\/\// end def device_should_exist? ( @new_resource.device != "none" ) && ( not network_device? ) && ( not %w[ cgroup tmpfs fuse ].include? @new_resource.fstype ) end private def device_fstab case @new_resource.device_type when :device @new_resource.device when :label "LABEL=#{@new_resource.device}" when :uuid "UUID=#{@new_resource.device}" end end def device_real if @real_device == nil if @new_resource.device_type == :device @real_device = @new_resource.device else @real_device = "" ret = shell_out("/sbin/findfs #{device_fstab}") device_line = ret.stdout.lines.first # stdout.first consumes @real_device = device_line.chomp unless device_line.nil? end end @real_device end def device_logstring case @new_resource.device_type when :device "#{device_real}" when :label "#{device_real} with label #{@new_resource.device}" when :uuid "#{device_real} with uuid #{@new_resource.device}" end end def device_mount_regex if network_device? # ignore trailing slash Regexp.escape(device_real)+"/?" elsif ::File.symlink?(device_real) # This regular expression tries to match device_real. If that does not match it will try to match the target of device_real. # So given a symlink like this: # /dev/mapper/vgroot-tmp.vol -> /dev/dm-9 # First it will try to match "/dev/mapper/vgroot-tmp.vol". If there is no match it will try matching for "/dev/dm-9". "(?:#{Regexp.escape(device_real)}|#{Regexp.escape(::File.expand_path(::File.readlink(device_real),::File.dirname(device_real)))})" else Regexp.escape(device_real) end end def device_fstab_regex if @new_resource.device_type == :device device_mount_regex else device_fstab end end def mount_options_unchanged? @current_resource.fstype == @new_resource.fstype and @current_resource.options == @new_resource.options and @current_resource.dump == @new_resource.dump and @current_resource.pass == @new_resource.pass end end end end end chef-12.3.0/lib/chef/provider/mount/solaris.rb0000644000004100000410000002371412520074675021221 0ustar www-datawww-data# Encoding: utf-8 # Author:: Hugo Fichter # Author:: Lamont Granquist () # Author:: Joshua Timberman () # Copyright:: Copyright (c) 2009-2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/provider/mount' require 'chef/log' require 'forwardable' class Chef class Provider class Mount # Mount Solaris File systems class Solaris < Chef::Provider::Mount extend Forwardable VFSTAB = '/etc/vfstab'.freeze def_delegator :@new_resource, :device, :device def_delegator :@new_resource, :device_type, :device_type def_delegator :@new_resource, :dump, :dump def_delegator :@new_resource, :fsck_device, :fsck_device def_delegator :@new_resource, :fstype, :fstype def_delegator :@new_resource, :mount_point, :mount_point def_delegator :@new_resource, :options, :options def_delegator :@new_resource, :pass, :pass def load_current_resource @current_resource = Chef::Resource::Mount.new(new_resource.name) current_resource.mount_point(mount_point) current_resource.device(device) current_resource.fsck_device(fsck_device) current_resource.device_type(device_type) update_current_resource_state end def define_resource_requirements requirements.assert(:mount, :remount) do |a| a.assertion { !device_should_exist? || ::File.exist?(device) } a.failure_message(Chef::Exceptions::Mount, "Device #{device} does not exist") a.whyrun("Assuming device #{device} would have been created") end unless fsck_device == '-' requirements.assert(:mount, :remount) do |a| a.assertion { ::File.exist?(fsck_device) } a.failure_message(Chef::Exceptions::Mount, "Device #{fsck_device} does not exist") a.whyrun("Assuming device #{fsck_device} would have been created") end end requirements.assert(:mount, :remount) do |a| a.assertion { ::File.exist?(mount_point) } a.failure_message(Chef::Exceptions::Mount, "Mount point #{mount_point} does not exist") a.whyrun("Assuming mount point #{mount_point} would have been created") end end def mount_fs actual_options = options || [] actual_options.delete('noauto') command = "mount -F #{fstype}" command << " -o #{actual_options.join(',')}" unless actual_options.empty? command << " #{device} #{mount_point}" shell_out!(command) end def umount_fs shell_out!("umount #{mount_point}") end def remount_fs # FIXME: Should remount always do the remount or only if the options change? actual_options = options || [] actual_options.delete('noauto') mount_options = actual_options.empty? ? '' : ",#{actual_options.join(',')}" shell_out!("mount -o remount#{mount_options} #{mount_point}") end def enable_fs unless mount_options_unchanged? # we are enabling because our options have changed, so disable first then re-enable. # XXX: this should be refactored to be the responsibility of the caller disable_fs if current_resource.enabled end vfstab_write(merge_vfstab_entry) end def disable_fs contents, found = delete_vfstab_entry if found vfstab_write(contents.reverse) else # this is likely some kind of internal error, since we should only call disable_fs when there # the filesystem we want to disable is enabled. Chef::Log.warn("#{new_resource} did not find the mountpoint to disable in the vfstab") end end def etc_tempfile yield Tempfile.open('vfstab', '/etc') end def mount_options_unchanged? new_options = options_remove_noauto(options) current_options = options_remove_noauto(current_resource.nil? ? nil : current_resource.options) current_resource.fsck_device == fsck_device && current_resource.fstype == fstype && current_options == new_options && current_resource.dump == dump && current_resource.pass == pass && current_resource.options.include?('noauto') == !mount_at_boot? end def update_current_resource_state current_resource.mounted(mounted?) (enabled, fstype, options, pass) = read_vfstab_status current_resource.enabled(enabled) current_resource.fstype(fstype) current_resource.options(options) current_resource.pass(pass) end def enabled? read_vfstab_status[0] end # Check for the device in mounttab. # on type on # /dev/dsk/c1t0d0s0 on / type ufs read/write/setuid/devices/intr/largefiles/logging/xattr/onerror=panic/dev=700040 on Tue May 1 11:33:55 2012 def mounted? mounted = false shell_out!('mount -v').stdout.each_line do |line| case line when /^#{device_regex}\s+on\s+#{Regexp.escape(mount_point)}\s+/ Chef::Log.debug("Special device #{device} is mounted as #{mount_point}") mounted = true when /^([\/\w]+)\son\s#{Regexp.escape(mount_point)}\s+/ Chef::Log.debug("Special device #{Regexp.last_match[1]} is mounted as #{mount_point}") mounted = false end end mounted end private def read_vfstab_status # Check to see if there is an entry in /etc/vfstab. Last entry for a volume wins. enabled = false fstype = options = pass = nil ::File.foreach(VFSTAB) do |line| case line when /^[#\s]/ next # solaris /etc/vfstab format: # device device mount FS fsck mount mount # to mount to fsck point type pass at boot options when /^#{device_regex}\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ enabled = true fstype = Regexp.last_match[1] options = Regexp.last_match[4] # Store the 'mount at boot' column from vfstab as the 'noauto' option # in current_resource.options (linux style) if Regexp.last_match[3] == 'no' if options.nil? || options.empty? options = 'noauto' else options += ',noauto' end end pass = (Regexp.last_match[2] == '-') ? 0 : Regexp.last_match[2].to_i Chef::Log.debug("Found mount #{device} to #{mount_point} in #{VFSTAB}") next when /^[-\/\w]+\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+/ # if we find a mountpoint on top of our mountpoint, then we are not enabled enabled = false Chef::Log.debug("Found conflicting mount point #{mount_point} in #{VFSTAB}") end end [enabled, fstype, options, pass] end def device_should_exist? !%w(tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs vxfs).include?(fstype) end def mount_at_boot? options.nil? || !options.include?('noauto') end def vfstab_write(contents) etc_tempfile do |f| f.write(contents.join('')) f.close # move, preserving modes of destination file mover = Chef::FileContentManagement::Deploy.strategy(true) mover.deploy(f.path, VFSTAB) end end def vfstab_entry actual_options = unless options.nil? tempops = options.dup tempops.delete('noauto') tempops end autostr = mount_at_boot? ? 'yes' : 'no' passstr = pass == 0 ? '-' : pass optstr = (actual_options.nil? || actual_options.empty?) ? '-' : actual_options.join(',') "\n#{device}\t#{fsck_device}\t#{mount_point}\t#{fstype}\t#{passstr}\t#{autostr}\t#{optstr}\n" end def delete_vfstab_entry contents = [] found = false ::File.readlines(VFSTAB).reverse_each do |line| if !found && line =~ /^#{device_regex}\s+\S+\s+#{Regexp.escape(mount_point)}/ found = true Chef::Log.debug("#{new_resource} is removed from vfstab") next end contents << line end [contents, found] end def merge_vfstab_entry contents = ::File.readlines(VFSTAB) contents[-1].chomp! contents << vfstab_entry end def options_remove_noauto(temp_options) new_options = [] new_options += temp_options.nil? ? [] : temp_options new_options.delete('noauto') new_options end def device_regex if ::File.symlink?(device) "(?:#{Regexp.escape(device)}|#{Regexp.escape(::File.expand_path(::File.readlink(device), ::File.dirname(device)))})" else Regexp.escape(device) end end end end end end chef-12.3.0/lib/chef/provider/mount/aix.rb0000644000004100000410000001557512520074675020334 0ustar www-datawww-data# # Author:: # 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/provider/mount' class Chef class Provider class Mount class Aix < Chef::Provider::Mount::Mount # Override for aix specific handling def initialize(new_resource, run_context) super # options and fstype are set to "defaults" and "auto" respectively in the Mount Resource class. These options are not valid for AIX, override them. if @new_resource.options[0] == "defaults" @new_resource.options.clear end if @new_resource.fstype == "auto" @new_resource.fstype = nil end end def enabled? # Check to see if there is an entry in /etc/filesystems. Last entry for a volume wins. Using command "lsfs" to fetch entries. enabled = false # lsfs o/p = #MountPoint:Device:Vfs:Nodename:Type:Size:Options:AutoMount:Acct # search only for current mount point shell_out("lsfs -c #{@new_resource.mount_point}").stdout.each_line do | line | case line when /^#\s/ next when /^#{Regexp.escape(@new_resource.mount_point)}:#{device_fstab_regex}:(\S+):(\[\S+\])?:(\S+)?:(\S+):(\S+):(\S+):(\S+)/ # mount point entry with ipv6 address for nodename (ipv6 address use ':') enabled = true @current_resource.fstype($1) @current_resource.options($5) Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/filesystems") next when /^#{Regexp.escape(@new_resource.mount_point)}:#{device_fstab_regex}::(\S+):(\S+)?:(\S+)?:(\S+):(\S+):(\S+):(\S+)/ # mount point entry with hostname or ipv4 address enabled = true @current_resource.fstype($1) @current_resource.options($5) Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/filesystems") next when /^#{Regexp.escape(@new_resource.mount_point)}/ enabled=false Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/filesystems") end end @current_resource.enabled(enabled) end def mounted? mounted = false shell_out!("mount").stdout.each_line do |line| if network_device? device_details = device_fstab.split(":") search_device = device_details[1] else search_device = device_fstab_regex end case line when /#{search_device}\s+#{Regexp.escape(@new_resource.mount_point)}/ mounted = true Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}") when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/ mounted = false Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab") end end @current_resource.mounted(mounted) end def mount_fs unless @current_resource.mounted mountable? command = "mount -v #{@new_resource.fstype}" if !(@new_resource.options.nil? || @new_resource.options.empty?) command << " -o #{@new_resource.options.join(',')}" end command << case @new_resource.device_type when :device " #{device_real}" when :label " -L #{@new_resource.device}" when :uuid " -U #{@new_resource.device}" end command << " #{@new_resource.mount_point}" shell_out!(command) Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}") else Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}") end end def remount_command if !(@new_resource.options.nil? || @new_resource.options.empty?) return "mount -o remount,#{@new_resource.options.join(',')} #{@new_resource.device} #{@new_resource.mount_point}" else return "mount -o remount #{@new_resource.device} #{@new_resource.mount_point}" end end def enable_fs if @current_resource.enabled && mount_options_unchanged? Chef::Log.debug("#{@new_resource} is already enabled - nothing to do") return nil end if @current_resource.enabled # The current options don't match what we have, so # disable, then enable. disable_fs end ::File.open("/etc/filesystems", "a") do |fstab| fstab.puts("#{@new_resource.mount_point}:") if network_device? device_details = device_fstab.split(":") fstab.puts("\tdev\t\t= #{device_details[1]}") fstab.puts("\tnodename\t\t= #{device_details[0]}") else fstab.puts("\tdev\t\t= #{device_fstab}") end fstab.puts("\tvfs\t\t= #{@new_resource.fstype}") fstab.puts("\tmount\t\t= false") fstab.puts "\toptions\t\t= #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty? Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}") end end def disable_fs contents = [] if @current_resource.enabled found_device = false ::File.open("/etc/filesystems", "r").each_line do |line| case line when /^\/.+:\s*$/ if line =~ /#{Regexp.escape(@new_resource.mount_point)}+:/ found_device = true else found_device = false end end if !found_device contents << line end end ::File.open("/etc/filesystems", "w") do |fstab| contents.each { |line| fstab.puts line} end else Chef::Log.debug("#{@new_resource} is not enabled - nothing to do") end end end end end end chef-12.3.0/lib/chef/provider/mount/windows.rb0000644000004100000410000000564012520074675021235 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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/mount' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/util/windows/net_use' require 'chef/util/windows/volume' end class Chef class Provider class Mount class Windows < Chef::Provider::Mount provides :mount, os: "windows" def is_volume(name) name =~ /^\\\\\?\\Volume\{[\w-]+\}\\$/ ? true : false end def initialize(new_resource, run_context) super @mount = nil end def load_current_resource if is_volume(@new_resource.device) @mount = Chef::Util::Windows::Volume.new(@new_resource.name) else #assume network drive @mount = Chef::Util::Windows::NetUse.new(@new_resource.name) end @current_resource = Chef::Resource::Mount.new(@new_resource.name) @current_resource.mount_point(@new_resource.mount_point) Chef::Log.debug("Checking for mount point #{@current_resource.mount_point}") begin @current_resource.device(@mount.device) Chef::Log.debug("#{@current_resource.device} mounted on #{@new_resource.mount_point}") @current_resource.mounted(true) rescue ArgumentError => e @current_resource.mounted(false) Chef::Log.debug("#{@new_resource.mount_point} is not mounted: #{e.message}") end end def mount_fs unless @current_resource.mounted @mount.add(:remote => @new_resource.device, :username => @new_resource.username, :domainname => @new_resource.domain, :password => @new_resource.password) Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}") else Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}") end end def umount_fs if @current_resource.mounted @mount.delete Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}") else Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}") end end end end end end chef-12.3.0/lib/chef/provider/package.rb0000644000004100000410000004714712520074675020004 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/mixin/shell_out' require 'chef/mixin/command' require 'chef/log' require 'chef/file_cache' require 'chef/platform' class Chef class Provider class Package < Chef::Provider include Chef::Mixin::Command include Chef::Mixin::ShellOut # # Hook that subclasses use to populate the candidate_version(s) # # @return [Array, String] candidate_version(s) may be a string or array attr_accessor :candidate_version def initialize(new_resource, run_context) super @candidate_version = nil end def whyrun_supported? true end def load_current_resource end def define_resource_requirements # XXX: upgrade with a specific version doesn't make a whole lot of sense, but why don't we throw this anyway if it happens? # if not, shouldn't we raise to tell the user to use install instead of upgrade if they want to pin a version? requirements.assert(:install) do |a| a.assertion { candidates_exist_for_all_forced_changes? } a.failure_message(Chef::Exceptions::Package, "No version specified, and no candidate version available for #{forced_packages_missing_candidates.join(", ")}") a.whyrun("Assuming a repository that offers #{forced_packages_missing_candidates.join(", ")} would have been configured") end # XXX: Does it make sense to pass in a source with :upgrade? Probably # not, but as with the above comment, we don't yet enforce such a thing, # so we'll just leave things as-is for now. requirements.assert(:upgrade, :install) do |a| a.assertion { candidates_exist_for_all_uninstalled? || new_resource.source } a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}") a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured") end end def action_install unless target_version_array.any? Chef::Log.debug("#{@new_resource} is already installed - nothing to do") return end # @todo: move the preseed code out of the base class (and complete the fix for Array of preseeds? ugh...) if @new_resource.response_file if preseed_file = get_preseed_file(package_names_for_targets, versions_for_targets) converge_by("preseed package #{package_names_for_targets}") do preseed_package(preseed_file) end end end # XXX: mutating the new resource is generally bad @new_resource.version(versions_for_new_resource) converge_by(install_description) do install_package(package_names_for_targets, versions_for_targets) Chef::Log.info("#{@new_resource} installed #{package_names_for_targets} at #{versions_for_targets}") end end def install_description description = [] target_version_array.each_with_index do |target_version, i| next if target_version.nil? package_name = package_name_array[i] description << "install version #{target_version} of package #{package_name}" end description end private :install_description def action_upgrade if !target_version_array.any? Chef::Log.debug("#{@new_resource} no versions to upgrade - nothing to do") return end # XXX: mutating the new resource is generally bad @new_resource.version(versions_for_new_resource) converge_by(upgrade_description) do upgrade_package(package_names_for_targets, versions_for_targets) log_allow_downgrade = allow_downgrade ? '(allow_downgrade)' : '' Chef::Log.info("#{@new_resource} upgraded#{log_allow_downgrade} #{package_names_for_targets} to #{versions_for_targets}") end end def upgrade_description log_allow_downgrade = allow_downgrade ? '(allow_downgrade)' : '' description = [] target_version_array.each_with_index do |target_version, i| next if target_version.nil? package_name = package_name_array[i] candidate_version = candidate_version_array[i] current_version = current_version_array[i] || "uninstalled" description << "upgrade#{log_allow_downgrade} package #{package_name} from #{current_version} to #{candidate_version}" end description end private :upgrade_description # @todo: ability to remove an array of packages def action_remove if removing_package? description = @new_resource.version ? "version #{@new_resource.version} of " : "" converge_by("remove #{description} package #{@current_resource.package_name}") do remove_package(@current_resource.package_name, @new_resource.version) Chef::Log.info("#{@new_resource} removed") end else Chef::Log.debug("#{@new_resource} package does not exist - nothing to do") end end def have_any_matching_version? f = [] new_version_array.each_with_index do |item, index| f << (item == current_version_array[index]) end f.any? end def removing_package? if !current_version_array.any? # ! any? means it's all nil's, which means nothing is installed false elsif !new_version_array.any? true # remove any version of all packages elsif have_any_matching_version? true # remove the version we have else false # we don't have the version we want to remove end end # @todo: ability to purge an array of packages def action_purge if removing_package? description = @new_resource.version ? "version #{@new_resource.version} of" : "" converge_by("purge #{description} package #{@current_resource.package_name}") do purge_package(@current_resource.package_name, @new_resource.version) Chef::Log.info("#{@new_resource} purged") end end end # @todo: ability to reconfigure an array of packages def action_reconfig if @current_resource.version == nil then Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do") return end unless @new_resource.response_file then Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do") return end if preseed_file = get_preseed_file(@new_resource.package_name, @current_resource.version) converge_by("reconfigure package #{@new_resource.package_name}") do preseed_package(preseed_file) reconfig_package(@new_resource.package_name, @current_resource.version) Chef::Log.info("#{@new_resource} reconfigured") end else Chef::Log.debug("#{@new_resource} preseeding has not changed - nothing to do") end end # @todo use composition rather than inheritance def install_package(name, version) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :install" end def upgrade_package(name, version) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :upgrade" end def remove_package(name, version) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remove" end def purge_package(name, version) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :purge" end def preseed_package(file) raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support pre-seeding package install/upgrade instructions" end def reconfig_package(name, version) raise( Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reconfig" ) end # this is heavily used by subclasses def expand_options(options) options ? " #{options}" : "" end # this is public and overridden by subclasses (rubygems package implements '>=' and '~>' operators) def target_version_already_installed?(current_version, new_version) new_version == current_version end # @todo: extract apt/dpkg specific preseeding to a helper class def get_preseed_file(name, version) resource = preseed_resource(name, version) resource.run_action(:create) Chef::Log.debug("#{@new_resource} fetched preseed file to #{resource.path}") if resource.updated_by_last_action? resource.path else false end end # @todo: extract apt/dpkg specific preseeding to a helper class def preseed_resource(name, version) # A directory in our cache to store this cookbook's preseed files in file_cache_dir = Chef::FileCache.create_cache_path("preseed/#{@new_resource.cookbook_name}") # The full path where the preseed file will be stored cache_seed_to = "#{file_cache_dir}/#{name}-#{version}.seed" Chef::Log.debug("#{@new_resource} fetching preseed file to #{cache_seed_to}") if template_available?(@new_resource.response_file) Chef::Log.debug("#{@new_resource} fetching preseed file via Template") remote_file = Chef::Resource::Template.new(cache_seed_to, run_context) remote_file.variables(@new_resource.response_file_variables) elsif cookbook_file_available?(@new_resource.response_file) Chef::Log.debug("#{@new_resource} fetching preseed file via cookbook_file") remote_file = Chef::Resource::CookbookFile.new(cache_seed_to, run_context) else message = "No template or cookbook file found for response file #{@new_resource.response_file}" raise Chef::Exceptions::FileNotFound, message end remote_file.cookbook_name = @new_resource.cookbook_name remote_file.source(@new_resource.response_file) remote_file.backup(false) remote_file end # helper method used by subclasses # def as_array(thing) [ thing ].flatten end private # Returns the package names which need to be modified. If the resource was called with an array of packages # then this will return an array of packages to update (may have 0 or 1 entries). If the resource was called # with a non-array package_name to manage then this will return a string rather than an Array. The output # of this is meant to be fed into subclass interfaces to install/upgrade packages and not all of them are # Array-aware. # # @return [String, Array] package_name(s) to actually update/install def package_names_for_targets package_names_for_targets = [] target_version_array.each_with_index do |target_version, i| next if target_version.nil? package_name = package_name_array[i] package_names_for_targets.push(package_name) end multipackage? ? package_names_for_targets : package_names_for_targets[0] end # Returns the package versions which need to be modified. If the resource was called with an array of packages # then this will return an array of versions to update (may have 0 or 1 entries). If the resource was called # with a non-array package_name to manage then this will return a string rather than an Array. The output # of this is meant to be fed into subclass interfaces to install/upgrade packages and not all of them are # Array-aware. # # @return [String, Array] package version(s) to actually update/install def versions_for_targets versions_for_targets = [] target_version_array.each_with_index do |target_version, i| next if target_version.nil? versions_for_targets.push(target_version) end multipackage? ? versions_for_targets : versions_for_targets[0] end # We need to mutate @new_resource.version() for some reason and this is a helper so that we inject the right # class (String or Array) into that attribute based on if we're handling an array of package names or not. # # @return [String, Array] target_versions coerced into the correct type for back-compat def versions_for_new_resource if multipackage? target_version_array else target_version_array[0] end end # Return an array indexed the same as *_version_array which contains either the target version to install/upgrade to # or else nil if the package is not being modified. # # @return [Array] array of package versions which need to be upgraded (nil = not being upgraded) def target_version_array @target_version_array ||= begin target_version_array = [] each_package do |package_name, new_version, current_version, candidate_version| case action when :upgrade if !candidate_version Chef::Log.debug("#{new_resource} #{package_name} has no candidate_version to upgrade to") target_version_array.push(nil) elsif current_version == candidate_version Chef::Log.debug("#{new_resource} #{package_name} the #{candidate_version} is already installed") target_version_array.push(nil) else Chef::Log.debug("#{new_resource} #{package_name} is out of date, will upgrade to #{candidate_version}") target_version_array.push(candidate_version) end when :install if new_version if target_version_already_installed?(current_version, new_version) Chef::Log.debug("#{new_resource} #{package_name} #{current_version} satisifies #{new_version} requirement") target_version_array.push(nil) else Chef::Log.debug("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}") target_version_array.push(new_version) end elsif current_version.nil? Chef::Log.debug("#{new_resource} #{package_name} not installed, installing #{candidate_version}") target_version_array.push(candidate_version) else Chef::Log.debug("#{new_resource} #{package_name} #{current_version} already installed") target_version_array.push(nil) end else # in specs please test the public interface provider.run_action(:install) instead of provider.action_install raise "internal error - target_version_array in package provider does not understand this action" end end target_version_array end end # Check the list of current_version_array and candidate_version_array. For any of the # packages if both versions are missing (uninstalled and no candidate) this will be an # unsolvable error. # # @return [Boolean] valid candidates exist for all uninstalled packages def candidates_exist_for_all_uninstalled? packages_missing_candidates.empty? end # Returns array of all packages which are missing candidate versions. # # @return [Array] names of packages missing candidates def packages_missing_candidates @packages_missing_candidates ||= begin missing = [] each_package do |package_name, new_version, current_version, candidate_version| missing.push(package_name) if candidate_version.nil? && current_version.nil? end missing end end # This looks for packages which have a new_version and a current_version, and they are # different (a "forced change") and for which there is no candidate. This is an edge # condition that candidates_exist_for_all_uninstalled? does not catch since in this case # it is not uninstalled but must be installed anyway and no version exists. # # @return [Boolean] valid candidates exist for all uninstalled packages def candidates_exist_for_all_forced_changes? forced_packages_missing_candidates.empty? end # Returns an array of all forced packages which are missing candidate versions # # @return [Array] names of packages missing candidates def forced_packages_missing_candidates @forced_packages_missing_candidates ||= begin missing = [] each_package do |package_name, new_version, current_version, candidate_version| next if new_version.nil? || current_version.nil? if candidate_version.nil? && !target_version_already_installed?(current_version, new_version) missing.push(package_name) end end missing end end # Helper to iterate over all the indexed *_array's in sync # # @yield [package_name, new_version, current_version, candidate_version] Description of block def each_package package_name_array.each_with_index do |package_name, i| candidate_version = candidate_version_array[i] current_version = current_version_array[i] new_version = new_version_array[i] yield package_name, new_version, current_version, candidate_version end end # @return [Boolean] if we're doing a multipackage install or not def multipackage? new_resource.package_name.is_a?(Array) end # @return [Array] package_name(s) as an array def package_name_array [ new_resource.package_name ].flatten end # @return [Array] candidate_version(s) as an array def candidate_version_array [ candidate_version ].flatten end # @return [Array] current_version(s) as an array def current_version_array [ current_resource.version ].flatten end # @return [Array] new_version(s) as an array def new_version_array @new_version_array ||= [ new_resource.version ].flatten.map do |v| ( v.nil? || v.empty? ) ? nil : v end end # @todo: extract apt/dpkg specific preseeding to a helper class def template_available?(path) run_context.has_template_in_cookbook?(new_resource.cookbook_name, path) end # @todo: extract apt/dpkg specific preseeding to a helper class def cookbook_file_available?(path) run_context.has_cookbook_file_in_cookbook?(new_resource.cookbook_name, path) end def allow_downgrade if @new_resource.respond_to?("allow_downgrade") @new_resource.allow_downgrade else false end end end end end chef-12.3.0/lib/chef/provider/group.rb0000644000004100000410000001360212520074675017532 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider' require 'chef/mixin/shell_out' require 'chef/mixin/command' require 'etc' class Chef class Provider class Group < Chef::Provider include Chef::Mixin::ShellOut include Chef::Mixin::Command attr_accessor :group_exists attr_accessor :change_desc def whyrun_supported? true end def initialize(new_resource, run_context) super @group_exists = true end def load_current_resource @current_resource = Chef::Resource::Group.new(@new_resource.name) @current_resource.group_name(@new_resource.group_name) group_info = nil begin group_info = Etc.getgrnam(@new_resource.group_name) rescue ArgumentError => e @group_exists = false Chef::Log.debug("#{@new_resource} group does not exist") end if group_info @new_resource.gid(group_info.gid) unless @new_resource.gid @current_resource.gid(group_info.gid) @current_resource.members(group_info.mem) end @current_resource end def define_resource_requirements requirements.assert(:modify) do |a| a.assertion { @group_exists } a.failure_message(Chef::Exceptions::Group, "Cannot modify #{@new_resource} - group does not exist!") a.whyrun("Group #{@new_resource} does not exist. Unless it would have been created earlier in this run, this attempt to modify it would fail.") end requirements.assert(:all_actions) do |a| # Make sure that the resource doesn't contain any common # user names in the members and exclude_members properties. if !@new_resource.members.nil? && !@new_resource.excluded_members.nil? common_members = @new_resource.members & @new_resource.excluded_members a.assertion { common_members.empty? } a.failure_message(Chef::Exceptions::ConflictingMembersInGroup, "Attempting to both add and remove users from a group: '#{common_members.join(', ')}'") # No why-run alternative end end end # Check to see if a group needs any changes. Populate # @change_desc with a description of why a change must occur # # ==== Returns # :: If a change is required # :: If a change is not required def compare_group @change_desc = [ ] if @new_resource.gid.to_s != @current_resource.gid.to_s @change_desc << "change gid #{@current_resource.gid} to #{@new_resource.gid}" end if(@new_resource.append) missing_members = [] @new_resource.members.each do |member| next if has_current_group_member?(member) missing_members << member end if missing_members.length > 0 @change_desc << "add missing member(s): #{missing_members.join(", ")}" end members_to_be_removed = [] @new_resource.excluded_members.each do |member| if has_current_group_member?(member) members_to_be_removed << member end end if members_to_be_removed.length > 0 @change_desc << "remove existing member(s): #{members_to_be_removed.join(", ")}" end else if @new_resource.members != @current_resource.members @change_desc << "replace group members with new list of members" end end !@change_desc.empty? end def has_current_group_member?(member) @current_resource.members.include?(member) end def action_create case @group_exists when false converge_by("create #{@new_resource.group_name}") do create_group Chef::Log.info("#{@new_resource} created") end else if compare_group converge_by(["alter group #{@new_resource.group_name}"] + change_desc) do manage_group Chef::Log.info("#{@new_resource} altered") end end end end def action_remove if @group_exists converge_by("remove group #{@new_resource.group_name}") do remove_group Chef::Log.info("#{@new_resource} removed") end end end def action_manage if @group_exists && compare_group converge_by(["manage group #{@new_resource.group_name}"] + change_desc) do manage_group Chef::Log.info("#{@new_resource} managed") end end end def action_modify if compare_group converge_by(["modify group #{@new_resource.group_name}"] + change_desc) do manage_group Chef::Log.info("#{@new_resource} modified") end end end def create_group raise NotImplementedError, "subclasses of Chef::Provider::Group should define #create_group" end def manage_group raise NotImplementedError, "subclasses of Chef::Provider::Group should define #manage_group" end def remove_group raise NotImplementedError, "subclasses of Chef::Provider::Group should define #remove_group" end end end end chef-12.3.0/lib/chef/provider/cookbook_file/0000755000004100000410000000000012520074675020654 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/cookbook_file/content.rb0000644000004100000410000000321412520074675022653 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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/file_content_management/content_base' require 'chef/file_content_management/tempfile' class Chef class Provider class CookbookFile class Content < Chef::FileContentManagement::ContentBase private def file_for_provider cookbook = run_context.cookbook_collection[resource_cookbook] file_cache_location = cookbook.preferred_filename_on_disk_location(run_context.node, :files, @new_resource.source) if file_cache_location.nil? nil else tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile tempfile.close Chef::Log.debug("#{@new_resource} staging #{file_cache_location} to #{tempfile.path}") FileUtils.cp(file_cache_location, tempfile.path) tempfile end end def resource_cookbook @new_resource.cookbook || @new_resource.cookbook_name end end end end end chef-12.3.0/lib/chef/provider/subversion.rb0000644000004100000410000002011212520074675020567 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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. # #TODO subversion and git should both extend from a base SCM provider. require 'chef/log' require 'chef/provider' require 'chef/mixin/command' require 'fileutils' class Chef class Provider class Subversion < Chef::Provider provides :subversion SVN_INFO_PATTERN = /^([\w\s]+): (.+)$/ include Chef::Mixin::Command def whyrun_supported? true end def load_current_resource @current_resource = Chef::Resource::Subversion.new(@new_resource.name) unless [:export, :force_export].include?(Array(@new_resource.action).first) if current_revision = find_current_revision @current_resource.revision current_revision end end end def define_resource_requirements requirements.assert(:all_actions) do |a| # Make sure the parent dir exists, or else fail. # for why run, print a message explaining the potential error. parent_directory = ::File.dirname(@new_resource.destination) a.assertion { ::File.directory?(parent_directory) } a.failure_message(Chef::Exceptions::MissingParentDirectory, "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{parent_directory} does not exist") a.whyrun("Directory #{parent_directory} does not exist, assuming it would have been created") end end def action_checkout if target_dir_non_existent_or_empty? converge_by("perform checkout of #{@new_resource.repository} into #{@new_resource.destination}") do shell_out!(checkout_command, run_options) end else Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do" end end def action_export if target_dir_non_existent_or_empty? action_force_export else Chef::Log.debug "#{@new_resource} export destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do" end end def action_force_export converge_by("export #{@new_resource.repository} into #{@new_resource.destination}") do shell_out!(export_command, run_options) end end def action_sync assert_target_directory_valid! if ::File.exist?(::File.join(@new_resource.destination, ".svn")) current_rev = find_current_revision Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{revision_int}" unless current_revision_matches_target_revision? converge_by("sync #{@new_resource.destination} from #{@new_resource.repository}") do shell_out!(sync_command, run_options) Chef::Log.info "#{@new_resource} updated to revision: #{revision_int}" end end else action_checkout end end def sync_command c = scm :update, @new_resource.svn_arguments, verbose, authentication, "-r#{revision_int}", @new_resource.destination Chef::Log.debug "#{@new_resource} updated working copy #{@new_resource.destination} to revision #{@new_resource.revision}" c end def checkout_command c = scm :checkout, @new_resource.svn_arguments, verbose, authentication, "-r#{revision_int}", @new_resource.repository, @new_resource.destination Chef::Log.info "#{@new_resource} checked out #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}" c end def export_command args = ["--force"] args << @new_resource.svn_arguments << verbose << authentication << "-r#{revision_int}" << @new_resource.repository << @new_resource.destination c = scm :export, *args Chef::Log.info "#{@new_resource} exported #{@new_resource.repository} at revision #{@new_resource.revision} to #{@new_resource.destination}" c end # If the specified revision isn't an integer ("HEAD" for example), look # up the revision id by asking the server # If the specified revision is an integer, trust it. def revision_int @revision_int ||= begin if @new_resource.revision =~ /^\d+$/ @new_resource.revision else command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}") status, svn_info, error_message = output_of_command(command, run_options) handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}") extract_revision_info(svn_info) end end end alias :revision_slug :revision_int def find_current_revision return nil unless ::File.exist?(::File.join(@new_resource.destination, ".svn")) command = scm(:info) status, svn_info, error_message = output_of_command(command, run_options(:cwd => cwd)) unless [0,1].include?(status.exitstatus) handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}") end extract_revision_info(svn_info) end def current_revision_matches_target_revision? (!@current_resource.revision.nil?) && (revision_int.strip.to_i == @current_resource.revision.strip.to_i) end def run_options(run_opts={}) run_opts[:user] = @new_resource.user if @new_resource.user run_opts[:group] = @new_resource.group if @new_resource.group run_opts[:timeout] = @new_resource.timeout if @new_resource.timeout run_opts end private def cwd @new_resource.destination end def verbose "-q" end def extract_revision_info(svn_info) repo_attrs = svn_info.lines.inject({}) do |attrs, line| if line =~ SVN_INFO_PATTERN property, value = $1, $2 attrs[property] = value end attrs end rev = (repo_attrs['Last Changed Rev'] || repo_attrs['Revision']) raise "Could not parse `svn info` data: #{svn_info}" if repo_attrs.empty? Chef::Log.debug "#{@new_resource} resolved revision #{@new_resource.revision} to #{rev}" rev end # If a username is configured for the SCM, return the command-line # switches for that. Note that we don't need to return the password # switch, since Capistrano will check for that prompt in the output # and will respond appropriately. def authentication return "" unless @new_resource.svn_username result = "--username #{@new_resource.svn_username} " result << "--password #{@new_resource.svn_password} " result end def scm(*args) ['svn', *args].compact.join(" ") end def target_dir_non_existent_or_empty? !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..'] end def assert_target_directory_valid! target_parent_directory = ::File.dirname(@new_resource.destination) unless ::File.directory?(target_parent_directory) msg = "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{target_parent_directory} does not exist" raise Chef::Exceptions::MissingParentDirectory, msg end end end end end chef-12.3.0/lib/chef/provider/reboot.rb0000644000004100000410000000407512520074675017674 0ustar www-datawww-data# # Author:: Chris Doherty ) # Copyright:: Copyright (c) 2014 Chef, 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/log' require 'chef/provider' class Chef class Provider class Reboot < Chef::Provider def whyrun_supported? true end def load_current_resource @current_resource ||= Chef::Resource::Reboot.new(@new_resource.name) @current_resource.reason(@new_resource.reason) @current_resource.delay_mins(@new_resource.delay_mins) @current_resource end def request_reboot node.run_context.request_reboot( :delay_mins => @new_resource.delay_mins, :reason => @new_resource.reason, :timestamp => Time.now, :requested_by => @new_resource.name ) end def action_request_reboot converge_by("request a system reboot to occur if the run succeeds") do Chef::Log.warn "Reboot requested:'#{@new_resource.name}'" request_reboot end end def action_reboot_now converge_by("rebooting the system immediately") do Chef::Log.warn "Rebooting system immediately, requested by '#{@new_resource.name}'" request_reboot throw :end_client_run_early end end def action_cancel converge_by("cancel any existing end-of-run reboot request") do Chef::Log.warn "Reboot canceled: '#{@new_resource.name}'" node.run_context.cancel_reboot end end end end end chef-12.3.0/lib/chef/provider/service/0000755000004100000410000000000012520074675017507 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/service/insserv.rb0000644000004100000410000000365212520074675021533 0ustar www-datawww-data# # Author:: Bryan McLellan # 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 'chef/provider/service/init' require 'chef/util/path_helper' class Chef class Provider class Service class Insserv < Chef::Provider::Service::Init provides :service, os: "linux" def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def load_current_resource super # Look for a /etc/rc.*/SnnSERVICE link to signify that the service would be started in a runlevel if Dir.glob("/etc/rc**/S*#{Chef::Util::PathHelper.escape_glob(current_resource.service_name)}").empty? current_resource.enabled false else current_resource.enabled true end current_resource end def enable_service() shell_out!("/sbin/insserv -r -f #{new_resource.service_name}") shell_out!("/sbin/insserv -d -f #{new_resource.service_name}") end def disable_service() shell_out!("/sbin/insserv -r -f #{new_resource.service_name}") end end end end end chef-12.3.0/lib/chef/provider/service/redhat.rb0000644000004100000410000000551612520074675021312 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/service/init' class Chef class Provider class Service class Redhat < Chef::Provider::Service::Init CHKCONFIG_ON = /\d:on/ CHKCONFIG_MISSING = /No such/ provides :service, platform_family: [ "rhel", "fedora", "suse" ] def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def initialize(new_resource, run_context) super @init_command = "/sbin/service #{@new_resource.service_name}" @new_resource.supports[:status] = true @service_missing = false end def define_resource_requirements shared_resource_requirements requirements.assert(:all_actions) do |a| chkconfig_file = "/sbin/chkconfig" a.assertion { ::File.exists? chkconfig_file } a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} does not exist!" end requirements.assert(:start, :enable, :reload, :restart) do |a| a.assertion { !@service_missing } a.failure_message Chef::Exceptions::Service, "#{@new_resource}: unable to locate the init.d script!" a.whyrun "Assuming service would be disabled. The init script is not presently installed." end end def load_current_resource super if ::File.exists?("/sbin/chkconfig") chkconfig = shell_out!("/sbin/chkconfig --list #{@current_resource.service_name}", :returns => [0,1]) @current_resource.enabled(!!(chkconfig.stdout =~ CHKCONFIG_ON)) @service_missing = !!(chkconfig.stderr =~ CHKCONFIG_MISSING) end @current_resource end def enable_service() shell_out! "/sbin/chkconfig #{@new_resource.service_name} on" end def disable_service() shell_out! "/sbin/chkconfig #{@new_resource.service_name} off" end end end end end chef-12.3.0/lib/chef/provider/service/debian.rb0000644000004100000410000001677112520074675021272 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/service/init' class Chef class Provider class Service class Debian < Chef::Provider::Service::Init UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i provides :service, platform_family: "debian" def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def load_current_resource super @priority_success = true @rcd_status = nil current_resource.priority(get_priority) current_resource.enabled(service_currently_enabled?(current_resource.priority)) current_resource end def define_resource_requirements # do not call super here, inherit only shared_requirements shared_resource_requirements requirements.assert(:all_actions) do |a| update_rcd = "/usr/sbin/update-rc.d" a.assertion { ::File.exists? update_rcd } a.failure_message Chef::Exceptions::Service, "#{update_rcd} does not exist!" # no whyrun recovery - this is a base system component of debian # distros and must be present end requirements.assert(:all_actions) do |a| a.assertion { @priority_success } a.failure_message Chef::Exceptions::Service, "/usr/sbin/update-rc.d -n -f #{current_resource.service_name} failed - #{@rcd_status.inspect}" # This can happen if the service is not yet installed,so we'll fake it. a.whyrun ["Unable to determine priority of service, assuming service would have been correctly installed earlier in the run.", "Assigning temporary priorities to continue.", "If this service is not properly installed prior to this point, this will fail."] do temp_priorities = {"6"=>[:stop, "20"], "0"=>[:stop, "20"], "1"=>[:stop, "20"], "2"=>[:start, "20"], "3"=>[:start, "20"], "4"=>[:start, "20"], "5"=>[:start, "20"]} current_resource.priority(temp_priorities) end end end def get_priority priority = {} @rcd_status = popen4("/usr/sbin/update-rc.d -n -f #{current_resource.service_name} remove") do |pid, stdin, stdout, stderr| [stdout, stderr].each do |iop| iop.each_line do |line| if UPDATE_RC_D_PRIORITIES =~ line # priority[runlevel] = [ S|K, priority ] # S = Start, K = Kill # debian runlevels: 0 Halt, 1 Singleuser, 2 Multiuser, 3-5 == 2, 6 Reboot priority[$1] = [($2 == "S" ? :start : :stop), $3] end if line =~ UPDATE_RC_D_ENABLED_MATCHES enabled = true end end end end # Reduce existing priority back to an integer if appropriate, picking # runlevel 2 as a baseline if priority[2] && [2..5].all? { |runlevel| priority[runlevel] == priority[2] } priority = priority[2].last end unless @rcd_status.exitstatus == 0 @priority_success = false end priority end def service_currently_enabled?(priority) enabled = false priority.each { |runlevel, arguments| Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}") # if we are in a update-rc.d default startup runlevel && we start in this runlevel if %w[ 1 2 3 4 5 S ].include?(runlevel) && arguments[0] == :start enabled = true end } enabled end # Override method from parent to ensure priority is up-to-date def action_enable if new_resource.priority.nil? priority_ok = true else priority_ok = @current_resource.priority == new_resource.priority end if current_resource.enabled and priority_ok Chef::Log.debug("#{new_resource} already enabled - nothing to do") else converge_by("enable service #{new_resource}") do enable_service Chef::Log.info("#{new_resource} enabled") end end load_new_resource_state new_resource.enabled(true) end def enable_service if new_resource.priority.is_a? Integer shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults #{new_resource.priority} #{100 - new_resource.priority}") elsif new_resource.priority.is_a? Hash # we call the same command regardless of we're enabling or disabling # users passing a Hash are responsible for setting their own start priorities set_priority else # No priority, go with update-rc.d defaults shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} defaults") end end def disable_service if new_resource.priority.is_a? Integer # Stop processes in reverse order of start using '100 - start_priority' shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop #{100 - new_resource.priority} 2 3 4 5 .") elsif new_resource.priority.is_a? Hash # we call the same command regardless of we're enabling or disabling # users passing a Hash are responsible for setting their own stop priorities set_priority else # no priority, using '100 - 20 (update-rc.d default)' to stop in reverse order of start shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} stop 80 2 3 4 5 .") end end def set_priority args = "" new_resource.priority.each do |level, o| action = o[0] priority = o[1] args += "#{action} #{priority} #{level} . " end shell_out!("/usr/sbin/update-rc.d -f #{new_resource.service_name} remove") shell_out!("/usr/sbin/update-rc.d #{new_resource.service_name} #{args}") end end end end end chef-12.3.0/lib/chef/provider/service/invokercd.rb0000644000004100000410000000254312520074675022024 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/service/init' class Chef class Provider class Service class Invokercd < Chef::Provider::Service::Init provides :service, platform_family: "debian" def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def initialize(new_resource, run_context) super @init_command = "/usr/sbin/invoke-rc.d #{@new_resource.service_name}" end end end end end chef-12.3.0/lib/chef/provider/service/upstart.rb0000644000004100000410000002253512520074675021545 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2010 Bryan McLellan # 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/service' require 'chef/provider/service/simple' require 'chef/mixin/command' require 'chef/util/file_edit' class Chef class Provider class Service class Upstart < Chef::Provider::Service::Simple UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/ provides :service, os: "linux" def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart) end # Upstart does more than start or stop a service, creating multiple 'states' [1] that a service can be in. # In chef, when we ask a service to start, we expect it to have started before performing the next step # since we have top down dependencies. Which is to say we may follow witha resource next that requires # that service to be running. According to [2] we can trust that sending a 'goal' such as start will not # return until that 'goal' is reached, or some error has occurred. # # [1] http://upstart.ubuntu.com/wiki/JobStates # [2] http://www.netsplit.com/2008/04/27/upstart-05-events/ def initialize(new_resource, run_context) # TODO: re-evaluate if this is needed after integrating cookbook fix raise ArgumentError, "run_context cannot be nil" unless run_context super run_context.node # dup so we can mutate @job @job = @new_resource.service_name.dup if @new_resource.parameters @new_resource.parameters.each do |key, value| @job << " #{key}=#{value}" end end platform, version = Chef::Platform.find_platform_and_version(run_context.node) if platform == "ubuntu" && (8.04..9.04).include?(version.to_f) @upstart_job_dir = "/etc/event.d" @upstart_conf_suffix = "" else @upstart_job_dir = "/etc/init" @upstart_conf_suffix = ".conf" end @command_success = true # new_resource.status_command= false, means upstart used @config_file_found = true @upstart_command_success = true end def define_resource_requirements # Do not call super, only call shared requirements shared_resource_requirements requirements.assert(:all_actions) do |a| if !@command_success whyrun_msg = @new_resource.status_command ? "Provided status command #{@new_resource.status_command} failed." : "Could not determine upstart state for service" end a.assertion { @command_success } # no failure here, just document the assumptions made. a.whyrun "#{whyrun_msg} Assuming service installed and not running." end requirements.assert(:all_actions) do |a| a.assertion { @config_file_found } # no failure here, just document the assumptions made. a.whyrun "Could not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}. Assuming service is disabled." end end def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) # Get running/stopped state # We do not support searching for a service via ps when using upstart since status is a native # upstart function. We will however support status_command in case someone wants to do something special. if @new_resource.status_command Chef::Log.debug("#{@new_resource} you have specified a status command, running..") begin if shell_out!(@new_resource.status_command) == 0 @current_resource.running true end rescue @command_success = false @current_resource.running false nil end else begin if upstart_state == "running" @current_resource.running true else @current_resource.running false end rescue Chef::Exceptions::Exec @command_success = false @current_resource.running false nil end end # Get enabled/disabled state by reading job configuration file if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}") Chef::Log.debug("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}") ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}",'r') do |file| while line = file.gets case line when /^start on/ Chef::Log.debug("#{@new_resource} enabled: #{line.chomp}") @current_resource.enabled true break when /^#start on/ Chef::Log.debug("#{@new_resource} disabled: #{line.chomp}") @current_resource.enabled false break end end end else @config_file_found = false Chef::Log.debug("#{@new_resource} did not find #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}") @current_resource.enabled false end @current_resource end def start_service # Calling start on a service that is already started will return 1 # Our 'goal' when we call start is to ensure the service is started if @current_resource.running Chef::Log.debug("#{@new_resource} already running, not starting") else if @new_resource.start_command super else shell_out_with_systems_locale!("/sbin/start #{@job}") end end end def stop_service # Calling stop on a service that is already stopped will return 1 # Our 'goal' when we call stop is to ensure the service is stopped unless @current_resource.running Chef::Log.debug("#{@new_resource} not running, not stopping") else if @new_resource.stop_command super else shell_out_with_systems_locale!("/sbin/stop #{@job}") end end end def restart_service if @new_resource.restart_command super # Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start. # Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883 else if @current_resource.running shell_out_with_systems_locale!("/sbin/restart #{@job}") else start_service end end end def reload_service if @new_resource.reload_command super else # upstart >= 0.6.3-4 supports reload (HUP) shell_out_with_systems_locale!("/sbin/reload #{@job}") end end # https://bugs.launchpad.net/upstart/+bug/94065 def enable_service Chef::Log.debug("#{@new_resource} upstart lacks inherent support for enabling services, editing job config file") conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}") conf.search_file_replace(/^#start on/, "start on") conf.write_file end def disable_service Chef::Log.debug("#{@new_resource} upstart lacks inherent support for disabling services, editing job config file") conf = Chef::Util::FileEdit.new("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}") conf.search_file_replace(/^start on/, "#start on") conf.write_file end def upstart_state command = "/sbin/status #{@job}" status = popen4(command) do |pid, stdin, stdout, stderr| stdout.each_line do |line| # rsyslog stop/waiting # service goal/state # OR # rsyslog (stop) waiting # service (goal) state line =~ UPSTART_STATE_FORMAT data = Regexp.last_match return data[2] end end end end end end end chef-12.3.0/lib/chef/provider/service/openbsd.rb0000644000004100000410000001734512520074675021500 0ustar www-datawww-data# # Author:: Scott Bonds () # Copyright:: Copyright (c) 2014 Scott Bonds # 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/mixin/command' require 'chef/mixin/shell_out' require 'chef/provider/service/init' require 'chef/resource/service' class Chef class Provider class Service class Openbsd < Chef::Provider::Service::Init provides :service, os: [ "openbsd" ] include Chef::Mixin::ShellOut attr_reader :init_command, :rc_conf, :rc_conf_local, :enabled_state_found RC_CONF_PATH = '/etc/rc.conf' RC_CONF_LOCAL_PATH = '/etc/rc.conf.local' def initialize(new_resource, run_context) super @rc_conf = ::File.read(RC_CONF_PATH) rescue '' @rc_conf_local = ::File.read(RC_CONF_LOCAL_PATH) rescue '' @init_command = ::File.exist?(rcd_script_path) ? rcd_script_path : nil new_resource.supports[:status] = true new_resource.status_command("#{default_init_command} check") end def load_current_resource @current_resource = Chef::Resource::Service.new(new_resource.name) current_resource.service_name(new_resource.service_name) Chef::Log.debug("#{current_resource} found at #{init_command}") determine_current_status! determine_enabled_status! current_resource end def define_resource_requirements shared_resource_requirements requirements.assert(:start, :enable, :reload, :restart) do |a| a.assertion { init_command } a.failure_message Chef::Exceptions::Service, "#{new_resource}: unable to locate the rc.d script" end requirements.assert(:all_actions) do |a| a.assertion { enabled_state_found } # for consistency with original behavior, this will not fail in non-whyrun mode; # rather it will silently set enabled state=>false a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled." end requirements.assert(:start, :enable, :reload, :restart) do |a| a.assertion { init_command && builtin_service_enable_variable_name != nil } a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{init_command} and rcvar" # No recovery in whyrun mode - the init file is present but not correct. end end def enable_service if !is_enabled? if is_builtin? if is_enabled_by_default? update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '') else # add line with blank string, which means enable update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"\"\n" end else # add to pkg_scripts, most recent addition goes last old_services_list = rc_conf_local.match(/^pkg_scripts="(.*)"/) old_services_list = old_services_list ? old_services_list[1].split(' ') : [] new_services_list = old_services_list + [new_resource.service_name] if rc_conf_local.match(/^pkg_scripts="(.*)"/) new_rcl = rc_conf_local.sub(/^pkg_scripts="(.*)"/, "pkg_scripts=\"#{new_services_list.join(' ')}\"") else new_rcl = rc_conf_local + "\n" + "pkg_scripts=\"#{new_services_list.join(' ')}\"\n" end update_rcl new_rcl end end end def disable_service if is_enabled? if is_builtin? if is_enabled_by_default? # add line to disable update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"NO\"\n" else # remove line to disable update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '') end else # remove from pkg_scripts old_list = rc_conf_local.match(/^pkg_scripts="(.*)"/) old_list = old_list ? old_list[1].split(' ') : [] new_list = old_list - [new_resource.service_name] update_rcl rc_conf_local.sub(/^pkg_scripts="(.*)"/, pkg_scripts="#{new_list.join(' ')}") end end end private def rcd_script_found? !init_command.nil? end def rcd_script_path "/etc/rc.d/#{new_resource.service_name}" end def update_rcl(value) FileUtils.touch RC_CONF_LOCAL_PATH if !::File.exists? RC_CONF_LOCAL_PATH ::File.write(RC_CONF_LOCAL_PATH, value) @rc_conf_local = value end # The variable name used in /etc/rc.conf.local for enabling this service def builtin_service_enable_variable_name @bsevn ||= begin result = nil if rcd_script_found? ::File.open(init_command) do |rcscript| if m = rcscript.read.match(/^# \$OpenBSD: (\w+)[(.rc),]?/) result = m[1] + "_flags" end end end # Fallback allows us to keep running in whyrun mode when # the script does not exist. result || new_resource.service_name end end def is_builtin? result = false var_name = builtin_service_enable_variable_name if var_name if rc_conf.match(/^#{Regexp.escape(var_name)}=(.*)/) result = true end end result end def is_enabled_by_default? result = false var_name = builtin_service_enable_variable_name if var_name if m = rc_conf.match(/^#{Regexp.escape(var_name)}=(.*)/) if !(m[1] =~ /"?[Nn][Oo]"?/) result = true end end end result end def determine_enabled_status! result = false # Default to disabled if the service doesn't currently exist at all @enabled_state_found = false if is_builtin? var_name = builtin_service_enable_variable_name if var_name if m = rc_conf_local.match(/^#{Regexp.escape(var_name)}=(.*)/) @enabled_state_found = true if !(m[1] =~ /"?[Nn][Oo]"?/) # e.g. looking for httpd_flags=NO result = true end end end if !@enabled_state_found result = is_enabled_by_default? end else var_name = @new_resource.service_name if var_name if m = rc_conf_local.match(/^pkg_scripts="(.*)"/) @enabled_state_found = true if m[1].include?(var_name) # e.g. looking for 'gdm' in pkg_scripts="gdm unbound" result = true end end end end current_resource.enabled result end alias :is_enabled? :determine_enabled_status! end end end end chef-12.3.0/lib/chef/provider/service/simple.rb0000644000004100000410000001563412520074675021336 0ustar www-datawww-data# # Author:: Mathieu Sauve-Frankel # Copyright:: Copyright (c) 2009 Mathieu Sauve-Frankel # 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/service' require 'chef/resource/service' require 'chef/mixin/command' class Chef class Provider class Service class Simple < Chef::Provider::Service # this must be subclassed to be useful so does not directly implement :service attr_reader :status_load_success def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) @status_load_success = true @ps_command_failed = false determine_current_status! @current_resource end def whyrun_supported? true end def shared_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { @status_load_success } a.whyrun ["Service status not available. Assuming a prior action would have installed the service.", "Assuming status of not running."] end end def define_resource_requirements # FIXME? need reload from service.rb shared_resource_requirements requirements.assert(:start) do |a| a.assertion { @new_resource.start_command } a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that start_command be set" end requirements.assert(:stop) do |a| a.assertion { @new_resource.stop_command } a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that stop_command be set" end requirements.assert(:restart) do |a| a.assertion { @new_resource.restart_command || ( @new_resource.start_command && @new_resource.stop_command ) } a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires a restart_command or both start_command and stop_command be set in order to perform a restart" end requirements.assert(:reload) do |a| a.assertion { @new_resource.reload_command } a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} requires a reload_command be set in order to perform a reload" end requirements.assert(:all_actions) do |a| a.assertion { @new_resource.status_command or @new_resource.supports[:status] or (!ps_cmd.nil? and !ps_cmd.empty?) } a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute" end requirements.assert(:all_actions) do |a| a.assertion { !@ps_command_failed } a.failure_message Chef::Exceptions::Service, "Command #{ps_cmd} failed to execute, cannot determine service current status" end end def start_service shell_out_with_systems_locale!(@new_resource.start_command) end def stop_service shell_out_with_systems_locale!(@new_resource.stop_command) end def restart_service if @new_resource.restart_command shell_out_with_systems_locale!(@new_resource.restart_command) else stop_service sleep 1 start_service end end def reload_service shell_out_with_systems_locale!(@new_resource.reload_command) end protected def determine_current_status! if @new_resource.status_command Chef::Log.debug("#{@new_resource} you have specified a status command, running..") begin if shell_out(@new_resource.status_command).exitstatus == 0 @current_resource.running true Chef::Log.debug("#{@new_resource} is running") end rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed. # Temporarily catching different types of exceptions here until we get Shellout fixed. # TODO: Remove the line before one we get the ShellOut fix. @status_load_success = false @current_resource.running false nil end elsif @new_resource.supports[:status] Chef::Log.debug("#{@new_resource} supports status, running") begin if shell_out("#{default_init_command} status").exitstatus == 0 @current_resource.running true Chef::Log.debug("#{@new_resource} is running") end # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed. # Temporarily catching different types of exceptions here until we get Shellout fixed. # TODO: Remove the line before one we get the ShellOut fix. rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError @status_load_success = false @current_resource.running false nil end else Chef::Log.debug "#{@new_resource} falling back to process table inspection" r = Regexp.new(@new_resource.pattern) Chef::Log.debug "#{@new_resource} attempting to match '#{@new_resource.pattern}' (#{r.inspect}) against process list" begin shell_out!(ps_cmd).stdout.each_line do |line| if r.match(line) @current_resource.running true break end end @current_resource.running false unless @current_resource.running Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}" # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed. # Temporarily catching different types of exceptions here until we get Shellout fixed. # TODO: Remove the line before one we get the ShellOut fix. rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError @ps_command_failed = true end end end def ps_cmd @run_context.node[:command] && @run_context.node[:command][:ps] end end end end end chef-12.3.0/lib/chef/provider/service/freebsd.rb0000644000004100000410000001547712520074675021464 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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/service' require 'chef/provider/service/init' require 'chef/mixin/command' class Chef class Provider class Service class Freebsd < Chef::Provider::Service::Init attr_reader :enabled_state_found provides :service, os: [ "freebsd", "netbsd" ] include Chef::Mixin::ShellOut def initialize(new_resource, run_context) super @enabled_state_found = false @init_command = nil if ::File.exist?("/etc/rc.d/#{new_resource.service_name}") @init_command = "/etc/rc.d/#{new_resource.service_name}" elsif ::File.exist?("/usr/local/etc/rc.d/#{new_resource.service_name}") @init_command = "/usr/local/etc/rc.d/#{new_resource.service_name}" end end def load_current_resource @current_resource = Chef::Resource::Service.new(new_resource.name) current_resource.service_name(new_resource.service_name) return current_resource unless init_command Chef::Log.debug("#{current_resource} found at #{init_command}") @status_load_success = true determine_current_status! # see Chef::Provider::Service::Simple determine_enabled_status! current_resource end def define_resource_requirements shared_resource_requirements requirements.assert(:start, :enable, :reload, :restart) do |a| a.assertion { init_command } a.failure_message Chef::Exceptions::Service, "#{new_resource}: unable to locate the rc.d script" a.whyrun("Assuming rc.d script will be installed by a previous action.") end requirements.assert(:all_actions) do |a| a.assertion { enabled_state_found } # for consistentcy with original behavior, this will not fail in non-whyrun mode; # rather it will silently set enabled state=>false a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled." end requirements.assert(:start, :enable, :reload, :restart) do |a| a.assertion { service_enable_variable_name != nil } a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{init_command} and rcvar" # No recovery in whyrun mode - the init file is present but not correct. end end def start_service if new_resource.start_command super else shell_out_with_systems_locale!("#{init_command} faststart") end end def stop_service if new_resource.stop_command super else shell_out_with_systems_locale!("#{init_command} faststop") end end def restart_service if new_resource.restart_command super elsif new_resource.supports[:restart] shell_out_with_systems_locale!("#{init_command} fastrestart") else stop_service sleep 1 start_service end end def enable_service set_service_enable("YES") unless current_resource.enabled end def disable_service set_service_enable("NO") if current_resource.enabled end private def read_rc_conf ::File.open("/etc/rc.conf", 'r') { |file| file.readlines } end def write_rc_conf(lines) ::File.open("/etc/rc.conf", 'w') do |file| lines.each { |line| file.puts(line) } end end # The variable name used in /etc/rc.conf for enabling this service def service_enable_variable_name @service_enable_variable_name ||= begin # Look for name="foo" in the shell script @init_command. Use this for determining the variable name in /etc/rc.conf # corresponding to this service # For example: to enable the service mysql-server with the init command /usr/local/etc/rc.d/mysql-server, you need # to set mysql_enable="YES" in /etc/rc.conf$ if init_command ::File.open(init_command) do |rcscript| rcscript.each_line do |line| if line =~ /^name="?(\w+)"?/ return $1 + "_enable" end end end # some scripts support multiple instances through symlinks such as openvpn. # We should get the service name from rcvar. Chef::Log.debug("name=\"service\" not found at #{init_command}. falling back to rcvar") sn = shell_out!("#{init_command} rcvar").stdout[/(\w+_enable)=/, 1] else # for why-run mode when the rcd_script is not there yet new_resource.service_name end end end def determine_enabled_status! var_name = service_enable_variable_name if ::File.exist?("/etc/rc.conf") && var_name read_rc_conf.each do |line| case line when /^#{Regexp.escape(var_name)}="(\w+)"/ enabled_state_found! if $1 =~ /^yes$/i current_resource.enabled true elsif $1 =~ /^(no|none)$/i current_resource.enabled false end end end end if current_resource.enabled.nil? Chef::Log.debug("#{new_resource.name} enable/disable state unknown") current_resource.enabled false end end def set_service_enable(value) lines = read_rc_conf # Remove line that set the old value lines.delete_if { |line| line =~ /^\#?\s*#{Regexp.escape(service_enable_variable_name)}=/ } # And append the line that sets the new value at the end lines << "#{service_enable_variable_name}=\"#{value}\"" write_rc_conf(lines) end def enabled_state_found! @enabled_state_found = true end end end end end chef-12.3.0/lib/chef/provider/service/init.rb0000644000004100000410000000565712520074675021014 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/provider/service/simple' require 'chef/mixin/command' class Chef class Provider class Service class Init < Chef::Provider::Service::Simple attr_accessor :init_command provides :service, os: "!windows" def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd) end def initialize(new_resource, run_context) super @init_command = "/etc/init.d/#{@new_resource.service_name}" end def define_resource_requirements # do not call super here, inherit only shared_requirements shared_resource_requirements requirements.assert(:start, :stop, :restart, :reload) do |a| a.assertion do custom_command_for_action?(action) || ::File.exist?(default_init_command) end a.failure_message(Chef::Exceptions::Service, "#{default_init_command} does not exist!") a.whyrun("Init script '#{default_init_command}' doesn't exist, assuming a prior action would have created it.") do # blindly assume that the service exists but is stopped in why run mode: @status_load_success = false end end end def start_service if @new_resource.start_command super else shell_out_with_systems_locale!("#{default_init_command} start") end end def stop_service if @new_resource.stop_command super else shell_out_with_systems_locale!("#{default_init_command} stop") end end def restart_service if @new_resource.restart_command super elsif @new_resource.supports[:restart] shell_out_with_systems_locale!("#{default_init_command} restart") else stop_service sleep 1 start_service end end def reload_service if @new_resource.reload_command super elsif @new_resource.supports[:reload] shell_out_with_systems_locale!("#{default_init_command} reload") end end end end end end chef-12.3.0/lib/chef/provider/service/solaris.rb0000644000004100000410000000562312520074675021516 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Copyright:: Copyright (c) 2010 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/service' require 'chef/resource/service' require 'chef/mixin/command' class Chef class Provider class Service class Solaris < Chef::Provider::Service attr_reader :maintenance provides :service, os: "solaris2" def initialize(new_resource, run_context=nil) super @init_command = "/usr/sbin/svcadm" @status_command = "/bin/svcs -l" @maintenace = false end def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) unless ::File.exists? "/bin/svcs" raise Chef::Exceptions::Service, "/bin/svcs does not exist!" end @status = service_status.enabled @current_resource end def enable_service shell_out!("#{default_init_command} clear #{@new_resource.service_name}") if @maintenance shell_out!("#{default_init_command} enable -s #{@new_resource.service_name}") end def disable_service shell_out!("#{default_init_command} disable -s #{@new_resource.service_name}") end alias_method :stop_service, :disable_service alias_method :start_service, :enable_service def reload_service shell_out_with_systems_locale!("#{default_init_command} refresh #{@new_resource.service_name}") end def restart_service ## svcadm restart doesn't supports sync(-s) option disable_service return enable_service end def service_status status = shell_out!("#{@status_command} #{@current_resource.service_name}", :returns => [0, 1]) status.stdout.each_line do |line| case line when /state\s+online/ @current_resource.enabled(true) @current_resource.running(true) when /state\s+maintenance/ @maintenance = true end end unless @current_resource.enabled @current_resource.enabled(false) @current_resource.running(false) end @current_resource end end end end end chef-12.3.0/lib/chef/provider/service/macosx.rb0000644000004100000410000002025712520074675021334 0ustar www-datawww-data# # Author:: Igor Afonov # Copyright:: Copyright (c) 2011 Igor Afonov # 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 'etc' require 'rexml/document' require 'chef/resource/service' require 'chef/resource/macosx_service' require 'chef/provider/service/simple' require 'chef/util/path_helper' class Chef class Provider class Service class Macosx < Chef::Provider::Service::Simple provides :service, os: "darwin" provides :macosx_service, os: "darwin" def self.gather_plist_dirs locations = %w{/Library/LaunchAgents /Library/LaunchDaemons /System/Library/LaunchAgents /System/Library/LaunchDaemons } Chef::Util::PathHelper.home('Library', 'LaunchAgents') { |p| locations << p } locations end PLIST_DIRS = gather_plist_dirs def load_current_resource @current_resource = Chef::Resource::MacosxService.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) @plist_size = 0 @plist = @new_resource.plist ? @new_resource.plist : find_service_plist @service_label = find_service_label # LauchAgents should be loaded as the console user. @console_user = @plist ? @plist.include?('LaunchAgents') : false @session_type = @new_resource.session_type if @console_user @console_user = Etc.getlogin Chef::Log.debug("#{new_resource} console_user: '#{@console_user}'") cmd = "su " param = !node['platform_version'].include?('10.10') ? '-l ' : '' @base_user_cmd = cmd + param + "#{@console_user} -c" # Default LauchAgent session should be Aqua @session_type = 'Aqua' if @session_type.nil? end Chef::Log.debug("#{new_resource} Plist: '#{@plist}' service_label: '#{@service_label}'") set_service_status @current_resource end def define_resource_requirements requirements.assert(:reload) do |a| a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload" end requirements.assert(:all_actions) do |a| a.assertion { @plist_size < 2 } a.failure_message Chef::Exceptions::Service, "Several plist files match service name. Please use full service name." end requirements.assert(:all_actions) do |a| a.assertion {::File.exists?(@plist.to_s) } a.failure_message Chef::Exceptions::Service, "Could not find plist for #{@new_resource}" end requirements.assert(:enable, :disable) do |a| a.assertion { !@service_label.to_s.empty? } a.failure_message Chef::Exceptions::Service, "Could not find service's label in plist file '#{@plist}'!" end requirements.assert(:all_actions) do |a| a.assertion { @plist_size > 0 } # No failure here in original code - so we also will not # fail. Instead warn that the service is potentially missing a.whyrun "Assuming that the service would have been previously installed and is currently disabled." do @current_resource.enabled(false) @current_resource.running(false) end end end def start_service if @current_resource.running Chef::Log.debug("#{@new_resource} already running, not starting") else if @new_resource.start_command super else load_service end end end def stop_service unless @current_resource.running Chef::Log.debug("#{@new_resource} not running, not stopping") else if @new_resource.stop_command super else unload_service end end end def restart_service if @new_resource.restart_command super else unload_service sleep 1 load_service end end # On OS/X, enabling a service has the side-effect of starting it, # and disabling a service has the side-effect of stopping it. # # This makes some sense on OS/X since launchctl is an "init"-style # supervisor that will restart daemons that are crashing, etc. def enable_service if @current_resource.enabled Chef::Log.debug("#{@new_resource} already enabled, not enabling") else load_service end end def disable_service unless @current_resource.enabled Chef::Log.debug("#{@new_resource} not enabled, not disabling") else unload_service end end def load_service session = @session_type ? "-S #{@session_type} " : '' cmd = 'launchctl load -w ' + session + @plist shell_out_as_user(cmd) end def unload_service cmd = 'launchctl unload -w ' + @plist shell_out_as_user(cmd) end def shell_out_as_user(cmd) if @console_user shell_out_with_systems_locale("#{@base_user_cmd} '#{cmd}'") else shell_out_with_systems_locale(cmd) end end def set_service_status return if @plist == nil or @service_label.to_s.empty? cmd = "launchctl list #{@service_label}" res = shell_out_as_user(cmd) if res.exitstatus == 0 @current_resource.enabled(true) else @current_resource.enabled(false) end if @current_resource.enabled res.stdout.each_line do |line| case line.downcase when /\s+\"pid\"\s+=\s+(\d+).*/ pid = $1 @current_resource.running(!pid.to_i.zero?) Chef::Log.debug("Current PID for #{@service_label} is #{pid}") end end else @current_resource.running(false) end end private def find_service_label # CHEF-5223 "you can't glob for a file that hasn't been converged # onto the node yet." return nil if @plist.nil? # Plist must exist by this point raise Chef::Exceptions::FileNotFound, "Cannot find #{@plist}!" unless ::File.exists?(@plist) # Most services have the same internal label as the name of the # plist file. However, there is no rule saying that *has* to be # the case, and some core services (notably, ssh) do not follow # this rule. # plist files can come in XML or Binary formats. this command # will make sure we get XML every time. plist_xml = shell_out_with_systems_locale!( "plutil -convert xml1 -o - #{@plist}" ).stdout plist_doc = REXML::Document.new(plist_xml) plist_doc.elements[ "/plist/dict/key[text()='Label']/following::string[1]/text()"] end def find_service_plist plists = PLIST_DIRS.inject([]) do |results, dir| edir = ::File.expand_path(dir) entries = Dir.glob( "#{edir}/*#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}*.plist" ) entries.any? ? results << entries : results end plists.flatten! @plist_size = plists.size plists.first end end end end end chef-12.3.0/lib/chef/provider/service/systemd.rb0000644000004100000410000000766612520074675021543 0ustar www-datawww-data# # Author:: Stephen Haynes () # 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 'chef/resource/service' require 'chef/provider/service/simple' require 'chef/mixin/which' class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple include Chef::Mixin::Which provides :service, os: "linux" attr_accessor :status_check_success def self.provides?(node, resource) super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd) end def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:systemd) end def load_current_resource @current_resource = Chef::Resource::Service.new(new_resource.name) current_resource.service_name(new_resource.service_name) @status_check_success = true if new_resource.status_command Chef::Log.debug("#{new_resource} you have specified a status command, running..") unless shell_out(new_resource.status_command).error? current_resource.running(true) else @status_check_success = false current_resource.running(false) current_resource.enabled(false) end else current_resource.running(is_active?) end current_resource.enabled(is_enabled?) current_resource end def define_resource_requirements shared_resource_requirements requirements.assert(:all_actions) do |a| a.assertion { status_check_success } # We won't stop in any case, but in whyrun warn and tell what we're doing. a.whyrun ["Failed to determine status of #{new_resource}, using command #{new_resource.status_command}.", "Assuming service would have been installed and is disabled"] end end def start_service if current_resource.running Chef::Log.debug("#{new_resource} already running, not starting") else if new_resource.start_command super else shell_out_with_systems_locale!("#{systemctl_path} start #{new_resource.service_name}") end end end def stop_service unless current_resource.running Chef::Log.debug("#{new_resource} not running, not stopping") else if new_resource.stop_command super else shell_out_with_systems_locale!("#{systemctl_path} stop #{new_resource.service_name}") end end end def restart_service if new_resource.restart_command super else shell_out_with_systems_locale!("#{systemctl_path} restart #{new_resource.service_name}") end end def reload_service if new_resource.reload_command super else if current_resource.running shell_out_with_systems_locale!("#{systemctl_path} reload #{new_resource.service_name}") else start_service end end end def enable_service shell_out!("#{systemctl_path} enable #{new_resource.service_name}") end def disable_service shell_out!("#{systemctl_path} disable #{new_resource.service_name}") end def is_active? shell_out("#{systemctl_path} is-active #{new_resource.service_name} --quiet").exitstatus == 0 end def is_enabled? shell_out("#{systemctl_path} is-enabled #{new_resource.service_name} --quiet").exitstatus == 0 end private def systemctl_path if @systemctl_path.nil? @systemctl_path = which("systemctl") end @systemctl_path end end chef-12.3.0/lib/chef/provider/service/aix.rb0000644000004100000410000001004612520074675020616 0ustar www-datawww-data# # Author:: kaustubh () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/provider/service' class Chef class Provider class Service class Aix < Chef::Provider::Service attr_reader :status_load_success provides :service, os: "aix" def initialize(new_resource, run_context) super end def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) @status_load_success = true @priority_success = true @is_resource_group = false determine_current_status! @current_resource end def whyrun_supported? true end def start_service if @is_resource_group shell_out!("startsrc -g #{@new_resource.service_name}") else shell_out!("startsrc -s #{@new_resource.service_name}") end end def stop_service if @is_resource_group shell_out!("stopsrc -g #{@new_resource.service_name}") else shell_out!("stopsrc -s #{@new_resource.service_name}") end end def restart_service stop_service start_service end def reload_service if @is_resource_group shell_out!("refresh -g #{@new_resource.service_name}") else shell_out!("refresh -s #{@new_resource.service_name}") end end def shared_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { @status_load_success } a.whyrun ["Service status not available. Assuming a prior action would have installed the service.", "Assuming status of not running."] end end def define_resource_requirements # FIXME? need reload from service.rb shared_resource_requirements end protected def determine_current_status! Chef::Log.debug "#{@new_resource} using lssrc to check the status " begin services = shell_out!("lssrc -a | grep -w #{@new_resource.service_name}").stdout.split("\n") is_resource_group?(services) if services.length == 1 && services[0].split(' ').last == "active" @current_resource.running true else @current_resource.running false end Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}" # ShellOut sometimes throws different types of Exceptions than ShellCommandFailed. # Temporarily catching different types of exceptions here until we get Shellout fixed. # TODO: Remove the line before one we get the ShellOut fix. rescue Mixlib::ShellOut::ShellCommandFailed, SystemCallError @status_load_success = false @current_resource.running false nil end end def is_resource_group? (services) if services.length > 1 Chef::Log.debug("#{@new_resource.service_name} is a group") @is_resource_group = true elsif services[0].split(' ')[1] == @new_resource.service_name Chef::Log.debug("#{@new_resource.service_name} is a group") @is_resource_group = true end end end end end end chef-12.3.0/lib/chef/provider/service/gentoo.rb0000644000004100000410000000457712520074675021344 0ustar www-datawww-data# # Author:: Lee Jensen () # Author:: AJ Christensen () # 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 'chef/provider/service/init' require 'chef/mixin/command' require 'chef/util/path_helper' class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init provides :service, platform_family: "gentoo" def load_current_resource @new_resource.supports[:status] = true @new_resource.supports[:restart] = true @found_script = false super @current_resource.enabled( Dir.glob("/etc/runlevels/**/#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}").any? do |file| @found_script = true exists = ::File.exists? file readable = ::File.readable? file Chef::Log.debug "#{@new_resource} exists: #{exists}, readable: #{readable}" exists and readable end ) Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}" @current_resource end def define_resource_requirements requirements.assert(:all_actions) do |a| a.assertion { ::File.exists?("/sbin/rc-update") } a.failure_message Chef::Exceptions::Service, "/sbin/rc-update does not exist" # no whyrun recovery -t his is a core component whose presence is # unlikely to be affected by what we do in the course of a chef run end requirements.assert(:all_actions) do |a| a.assertion { @found_script } # No failure, just informational output from whyrun a.whyrun "Could not find service #{@new_resource.service_name} under any runlevel" end end def enable_service() shell_out!("/sbin/rc-update add #{@new_resource.service_name} default") end def disable_service() shell_out!("/sbin/rc-update del #{@new_resource.service_name} default") end end chef-12.3.0/lib/chef/provider/service/windows.rb0000644000004100000410000002736412520074675021542 0ustar www-datawww-data# # Author:: Nuo Yan # Author:: Bryan McLellan # Author:: Seth Chisamore # Copyright:: Copyright (c) 2010-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 'chef/provider/service/simple' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/win32/error' require 'win32/service' end class Chef::Provider::Service::Windows < Chef::Provider::Service provides :service, os: "windows" provides :windows_service, os: "windows" include Chef::Mixin::ShellOut include Chef::ReservedNames::Win32::API::Error rescue LoadError #Win32::Service.get_start_type AUTO_START = 'auto start' MANUAL = 'demand start' DISABLED = 'disabled' #Win32::Service.get_current_state RUNNING = 'running' STOPPED = 'stopped' CONTINUE_PENDING = 'continue pending' PAUSE_PENDING = 'pause pending' PAUSED = 'paused' START_PENDING = 'start pending' STOP_PENDING = 'stop pending' TIMEOUT = 60 def whyrun_supported? false end def load_current_resource @current_resource = Chef::Resource::WindowsService.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) @current_resource.running(current_state == RUNNING) Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}" case current_start_type when AUTO_START @current_resource.enabled(true) when DISABLED @current_resource.enabled(false) end Chef::Log.debug "#{@new_resource} enabled: #{@current_resource.enabled}" @current_resource end def start_service if Win32::Service.exists?(@new_resource.service_name) # reconfiguration is idempotent, so just do it. new_config = { service_name: @new_resource.service_name, service_start_name: @new_resource.run_as_user, password: @new_resource.run_as_password, }.reject { |k,v| v.nil? || v.length == 0 } Win32::Service.configure(new_config) Chef::Log.info "#{@new_resource} configured with #{new_config.inspect}" # it would be nice to check if the user already has the logon privilege, but that turns out to be # nontrivial. if new_config.has_key?(:service_start_name) grant_service_logon(new_config[:service_start_name]) end state = current_state if state == RUNNING Chef::Log.debug "#{@new_resource} already started - nothing to do" elsif state == START_PENDING Chef::Log.debug "#{@new_resource} already sent start signal - waiting for start" wait_for_state(RUNNING) elsif state == STOPPED if @new_resource.start_command Chef::Log.debug "#{@new_resource} starting service using the given start_command" shell_out!(@new_resource.start_command) else spawn_command_thread do begin Win32::Service.start(@new_resource.service_name) rescue SystemCallError => ex if ex.errno == ERROR_SERVICE_LOGON_FAILED Chef::Log.error ex.message raise Chef::Exceptions::Service, "Service #{@new_resource} did not start due to a logon failure (error #{ERROR_SERVICE_LOGON_FAILED}): possibly the specified user '#{@new_resource.run_as_user}' does not have the 'log on as a service' privilege, or the password is incorrect." else raise ex end end end wait_for_state(RUNNING) end @new_resource.updated_by_last_action(true) else raise Chef::Exceptions::Service, "Service #{@new_resource} can't be started from state [#{state}]" end else Chef::Log.debug "#{@new_resource} does not exist - nothing to do" end end def stop_service if Win32::Service.exists?(@new_resource.service_name) state = current_state if state == RUNNING if @new_resource.stop_command Chef::Log.debug "#{@new_resource} stopping service using the given stop_command" shell_out!(@new_resource.stop_command) else spawn_command_thread do Win32::Service.stop(@new_resource.service_name) end wait_for_state(STOPPED) end @new_resource.updated_by_last_action(true) elsif state == STOPPED Chef::Log.debug "#{@new_resource} already stopped - nothing to do" elsif state == STOP_PENDING Chef::Log.debug "#{@new_resource} already sent stop signal - waiting for stop" wait_for_state(STOPPED) else raise Chef::Exceptions::Service, "Service #{@new_resource} can't be stopped from state [#{state}]" end else Chef::Log.debug "#{@new_resource} does not exist - nothing to do" end end def restart_service if Win32::Service.exists?(@new_resource.service_name) if @new_resource.restart_command Chef::Log.debug "#{@new_resource} restarting service using the given restart_command" shell_out!(@new_resource.restart_command) else stop_service start_service end @new_resource.updated_by_last_action(true) else Chef::Log.debug "#{@new_resource} does not exist - nothing to do" end end def enable_service if Win32::Service.exists?(@new_resource.service_name) set_startup_type(:automatic) else Chef::Log.debug "#{@new_resource} does not exist - nothing to do" end end def disable_service if Win32::Service.exists?(@new_resource.service_name) set_startup_type(:disabled) else Chef::Log.debug "#{@new_resource} does not exist - nothing to do" end end def action_enable if current_start_type != AUTO_START converge_by("enable service #{@new_resource}") do enable_service Chef::Log.info("#{@new_resource} enabled") end else Chef::Log.debug("#{@new_resource} already enabled - nothing to do") end load_new_resource_state @new_resource.enabled(true) end def action_disable if current_start_type != DISABLED converge_by("disable service #{@new_resource}") do disable_service Chef::Log.info("#{@new_resource} disabled") end else Chef::Log.debug("#{@new_resource} already disabled - nothing to do") end load_new_resource_state @new_resource.enabled(false) end def action_configure_startup case @new_resource.startup_type when :automatic if current_start_type != AUTO_START converge_by("set service #{@new_resource} startup type to automatic") do set_startup_type(:automatic) end else Chef::Log.debug("#{@new_resource} startup_type already automatic - nothing to do") end when :manual if current_start_type != MANUAL converge_by("set service #{@new_resource} startup type to manual") do set_startup_type(:manual) end else Chef::Log.debug("#{@new_resource} startup_type already manual - nothing to do") end when :disabled if current_start_type != DISABLED converge_by("set service #{@new_resource} startup type to disabled") do set_startup_type(:disabled) end else Chef::Log.debug("#{@new_resource} startup_type already disabled - nothing to do") end end # Avoid changing enabled from true/false for now @new_resource.enabled(nil) end private def make_policy_text(username) text = <<-EOS [Unicode] Unicode=yes [Privilege Rights] SeServiceLogonRight = \\\\#{canonicalize_username(username)},*S-1-5-80-0 [Version] signature="$CHICAGO$" Revision=1 EOS end def grant_logfile_name(username) Chef::Util::PathHelper.canonical_path("#{Dir.tmpdir}/logon_grant-#{clean_username_for_path(username)}-#{$$}.log", prefix=false) end def grant_policyfile_name(username) Chef::Util::PathHelper.canonical_path("#{Dir.tmpdir}/service_logon_policy-#{clean_username_for_path(username)}-#{$$}.inf", prefix=false) end def grant_dbfile_name(username) "#{ENV['TEMP']}\\secedit.sdb" end def grant_service_logon(username) logfile = grant_logfile_name(username) policy_file = ::File.new(grant_policyfile_name(username), 'w') policy_text = make_policy_text(username) dbfile = grant_dbfile_name(username) # this is just an audit file. begin Chef::Log.debug "Policy file text:\n#{policy_text}" policy_file.puts(policy_text) policy_file.close # need to flush the buffer. # it would be nice to do this with APIs instead, but the LSA_* APIs are # particularly onerous and life is short. cmd = %Q{secedit.exe /configure /db "#{dbfile}" /cfg "#{policy_file.path}" /areas USER_RIGHTS SECURITYPOLICY SERVICES /log "#{logfile}"} Chef::Log.debug "Granting logon-as-service privilege with: #{cmd}" runner = shell_out(cmd) if runner.exitstatus != 0 Chef::Log.fatal "Logon-as-service grant failed with output: #{runner.stdout}" raise Chef::Exceptions::Service, <<-EOS Logon-as-service grant failed with policy file #{policy_file.path}. You can look at #{logfile} for details, or do `secedit /analyze #{dbfile}`. The failed command was `#{cmd}`. EOS end Chef::Log.info "Grant logon-as-service to user '#{username}' successful." ::File.delete(dbfile) rescue nil ::File.delete(policy_file) ::File.delete(logfile) rescue nil # logfile is not always present at end. end true end # remove characters that make for broken or wonky filenames. def clean_username_for_path(username) username.gsub(/[\/\\. ]+/, '_') end # the security policy file only seems to accept \\username, so fix .\username or .\\username. # TODO: this probably has to be fixed to handle various valid Windows names correctly. def canonicalize_username(username) username.sub(/^\.?\\+/, '') end def current_state Win32::Service.status(@new_resource.service_name).current_state end def current_start_type Win32::Service.config_info(@new_resource.service_name).start_type 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) retries = 0 loop do break if current_state == desired_state raise Timeout::Error if ( retries += 1 ) > resource_timeout sleep 1 end end def resource_timeout @resource_timeout ||= @new_resource.timeout || TIMEOUT end def spawn_command_thread worker = Thread.new do yield end Timeout.timeout(resource_timeout) do worker.join end end # Takes Win32::Service start_types def set_startup_type(type) # Set-Service Startup Type => Win32::Service Constant allowed_types = { :automatic => Win32::Service::AUTO_START, :manual => Win32::Service::DEMAND_START, :disabled => Win32::Service::DISABLED } unless allowed_types.keys.include?(type) raise Chef::Exceptions::ConfigurationError, "#{@new_resource.name}: Startup type '#{type}' is not supported" end Chef::Log.debug "#{@new_resource.name} setting start_type to #{type}" Win32::Service.configure( :service_name => @new_resource.service_name, :start_type => allowed_types[type] ) @new_resource.updated_by_last_action(true) end end chef-12.3.0/lib/chef/provider/service/aixinit.rb0000644000004100000410000000767112520074675021514 0ustar www-datawww-data# # Author:: kaustubh () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/provider/service/init' class Chef class Provider class Service class AixInit < Chef::Provider::Service::Init RC_D_SCRIPT_NAME = /\/etc\/rc.d\/rc2.d\/([SK])(\d\d|)/i def initialize(new_resource, run_context) super @init_command = "/etc/rc.d/init.d/#{@new_resource.service_name}" end def load_current_resource super @priority_success = true @rcd_status = nil set_current_resource_attributes @current_resource end def action_enable if @new_resource.priority.nil? priority_ok = true else priority_ok = @current_resource.priority == @new_resource.priority end if @current_resource.enabled and priority_ok Chef::Log.debug("#{@new_resource} already enabled - nothing to do") else converge_by("enable service #{@new_resource}") do enable_service Chef::Log.info("#{@new_resource} enabled") end end load_new_resource_state @new_resource.enabled(true) end def enable_service Dir.glob(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).each { |f| ::File.delete(f)} if @new_resource.priority.is_a? Integer create_symlink(2, 'S', @new_resource.priority) elsif @new_resource.priority.is_a? Hash @new_resource.priority.each do |level,o| create_symlink(level,(o[0] == :start ? 'S' : 'K'),o[1]) end else create_symlink(2, 'S', '') end end def disable_service Dir.glob(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).each { |f| ::File.delete(f) } if @new_resource.priority.is_a? Integer create_symlink(2, 'K',100 - @new_resource.priority) elsif @new_resource.priority.is_a? Hash @new_resource.priority.each do |level,o| create_symlink(level, 'K', 100 - o[1]) if o[0] == :stop end else create_symlink(2, 'K', '') end end def create_symlink(run_level, status, priority) ::File.symlink("/etc/rc.d/init.d/#{@new_resource.service_name}", "/etc/rc.d/rc#{run_level}.d/#{status}#{priority}#{@new_resource.service_name}") end def set_current_resource_attributes # assuming run level 2 for aix is_enabled = false files = Dir.glob(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]) priority = {} files.each do |file| if (RC_D_SCRIPT_NAME =~ file) priority[2] = [($1 == "S" ? :start : :stop), ($2.empty? ? '' : $2.to_i)] if $1 == "S" is_enabled = true end end end if is_enabled && files.length == 1 priority = priority[2][1] end @current_resource.enabled(is_enabled) @current_resource.priority(priority) end end end end end chef-12.3.0/lib/chef/provider/service/arch.rb0000644000004100000410000000720712520074675020757 0ustar www-datawww-data# # Author:: Jan Zimmek () # Copyright:: Copyright (c) 2010 Jan Zimmek # 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/service/init' class Chef::Provider::Service::Arch < Chef::Provider::Service::Init provides :service, platform_family: "arch" def self.supports?(resource, action) Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:etc_rcd) end def initialize(new_resource, run_context) super @init_command = "/etc/rc.d/#{@new_resource.service_name}" end def load_current_resource raise Chef::Exceptions::Service, "Could not find /etc/rc.conf" unless ::File.exists?("/etc/rc.conf") raise Chef::Exceptions::Service, "No DAEMONS found in /etc/rc.conf" unless ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m) super @current_resource.enabled(daemons.include?(@current_resource.service_name)) @current_resource end # Get list of all daemons from the file '/etc/rc.conf'. # Mutiple lines and background form are supported. Example: # DAEMONS=(\ # foobar \ # @example \ # !net \ # ) def daemons entries = [] if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m) entries += $1.gsub(/\\?[\r\n]/, ' ').gsub(/# *[^ ]+/,' ').split(' ') if $1.length > 0 end yield(entries) if block_given? entries end # FIXME: Multiple entries of DAEMONS will cause very bad results :) def update_daemons(entries) content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(' ')})") ::File.open("/etc/rc.conf", "w") do |f| f.write(content) end end def enable_service() new_daemons = [] entries = daemons if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}") # exists and already enabled (or already enabled as a background service) # new_daemons += entries else if entries.include?("!#{new_resource.service_name}") # exists but disabled entries.each do |daemon| if daemon == "!#{new_resource.service_name}" new_daemons << new_resource.service_name else new_daemons << daemon end end else # does not exist new_daemons += entries new_daemons << new_resource.service_name end update_daemons(new_daemons) end end def disable_service() new_daemons = [] entries = daemons if entries.include?("!#{new_resource.service_name}") # exists and disabled # new_daemons += entries else if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}") # exists but enabled (or enabled as a back-ground service) # FIXME: Does arch support !@foobar ? entries.each do |daemon| if [new_resource.service_name, "@#{new_resource.service_name}"].include?(daemon) new_daemons << "!#{new_resource.service_name}" else new_daemons << daemon end end end update_daemons(new_daemons) end end end chef-12.3.0/lib/chef/provider/remote_file.rb0000644000004100000410000000306512520074675020672 0ustar www-datawww-data# # Author:: Jesse Campbell () # Author:: Adam Jacob () # 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 'chef/provider/file' require 'chef/deprecation/provider/remote_file' require 'chef/deprecation/warnings' class Chef class Provider class RemoteFile < Chef::Provider::File extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::RemoteFile add_deprecation_warnings_for(Chef::Deprecation::Provider::RemoteFile.instance_methods) def initialize(new_resource, run_context) @content_class = Chef::Provider::RemoteFile::Content super end def load_current_resource @current_resource = Chef::Resource::RemoteFile.new(@new_resource.name) super end private def managing_content? return true if @new_resource.checksum return true if !@new_resource.source.nil? && @action != :create_if_missing false end end end end chef-12.3.0/lib/chef/provider/env.rb0000644000004100000410000001235712520074675017174 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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' require 'chef/mixin/command' require 'chef/resource/env' class Chef class Provider class Env < Chef::Provider include Chef::Mixin::Command attr_accessor :key_exists provides :env, os: "!windows" def initialize(new_resource, run_context) super @key_exists = true end def load_current_resource @current_resource = Chef::Resource::Env.new(@new_resource.name) @current_resource.key_name(@new_resource.key_name) if env_key_exists(@new_resource.key_name) @current_resource.value(env_value(@new_resource.key_name)) else @key_exists = false Chef::Log.debug("#{@new_resource} key does not exist") end @current_resource end def env_value(key_name) raise Chef::Exceptions::Env, "#{self.to_s} provider does not implement env_value!" end def env_key_exists(key_name) env_value(key_name) ? true : false end # Check to see if value needs any changes # # ==== Returns # :: If a change is required # :: If a change is not required def requires_modify_or_create? if @new_resource.delim #e.g. check for existing value within PATH new_values.inject(0) do |index, val| next_index = current_values.find_index val return true if next_index.nil? || next_index < index next_index end false else @new_resource.value != @current_resource.value end end alias_method :compare_value, :requires_modify_or_create? def action_create if @key_exists if requires_modify_or_create? modify_env Chef::Log.info("#{@new_resource} altered") @new_resource.updated_by_last_action(true) end else create_env Chef::Log.info("#{@new_resource} created") @new_resource.updated_by_last_action(true) end end #e.g. delete a PATH element # # ==== Returns # :: If we handled the element case and caller should not delete the key # :: Caller should delete the key, either no :delim was specific or value was empty # after we removed the element. def delete_element return false unless @new_resource.delim #no delim: delete the key needs_delete = new_values.any? { |v| current_values.include?(v) } if !needs_delete Chef::Log.debug("#{@new_resource} element '#{@new_resource.value}' does not exist") return true #do not delete the key else new_value = current_values.select do |item| not new_values.include?(item) end.join(@new_resource.delim) if new_value.empty? return false #nothing left here, delete the key else old_value = @new_resource.value(new_value) create_env Chef::Log.debug("#{@new_resource} deleted #{old_value} element") @new_resource.updated_by_last_action(true) return true #we removed the element and updated; do not delete the key end end end def action_delete if @key_exists && !delete_element delete_env Chef::Log.info("#{@new_resource} deleted") @new_resource.updated_by_last_action(true) end end def action_modify if @key_exists if requires_modify_or_create? modify_env Chef::Log.info("#{@new_resource} modified") @new_resource.updated_by_last_action(true) end else raise Chef::Exceptions::Env, "Cannot modify #{@new_resource} - key does not exist!" end end def create_env raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :#{@new_resource.action}" end def delete_env raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :delete" end def modify_env if @new_resource.delim @new_resource.value((new_values + current_values).uniq.join(@new_resource.delim)) end create_env end # Returns the current values to split by delimiter def current_values @current_values ||= @current_resource.value.split(@new_resource.delim) end # Returns the new values to split by delimiter def new_values @new_values ||= @new_resource.value.split(@new_resource.delim) end end end end chef-12.3.0/lib/chef/provider/template_finder.rb0000644000004100000410000000317112520074675021540 0ustar www-datawww-data#-- # Author:: Andrea Campi () # 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. # class Chef class Provider class TemplateFinder def initialize(run_context, cookbook_name, node) @run_context = run_context @cookbook_name = cookbook_name @node = node end def find(template_name, options = {}) template_name = template_source_name(template_name, options) if options[:local] return template_name end cookbook_name = find_cookbook_name(options) cookbook = @run_context.cookbook_collection[cookbook_name] cookbook.preferred_filename_on_disk_location(@node, :templates, template_name) end protected def template_source_name(name, options) if options[:source] options[:source] else name end end def find_cookbook_name(options) if options[:cookbook] options[:cookbook] else @cookbook_name end end end end end chef-12.3.0/lib/chef/provider/deploy.rb0000644000004100000410000004217612520074675017702 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 "chef/mixin/command" require "chef/mixin/from_file" require "chef/provider/git" require "chef/provider/subversion" require "chef/dsl/recipe" require "chef/util/path_helper" class Chef class Provider class Deploy < Chef::Provider include Chef::DSL::Recipe include Chef::Mixin::FromFile include Chef::Mixin::Command attr_reader :scm_provider, :release_path, :shared_path, :previous_release_path def initialize(new_resource, run_context) super(new_resource, run_context) # will resolve to either git or svn based on resource attributes, # and will create a resource corresponding to that provider @scm_provider = new_resource.scm_provider.new(new_resource, run_context) # @configuration is not used by Deploy, it is only for backwards compat with # chef-deploy or capistrano hooks that might use it to get environment information @configuration = @new_resource.to_hash @configuration[:environment] = @configuration[:environment] && @configuration[:environment]["RAILS_ENV"] end def whyrun_supported? true end def load_current_resource @scm_provider.load_current_resource @release_path = @new_resource.deploy_to + "/releases/#{release_slug}" @shared_path = @new_resource.shared_path end def sudo(command,&block) execute(command, &block) end def run(command, &block) exec = execute(command, &block) exec.user(@new_resource.user) if @new_resource.user exec.group(@new_resource.group) if @new_resource.group exec.cwd(release_path) unless exec.cwd exec.environment(@new_resource.environment) unless exec.environment converge_by("execute #{command}") do exec end end def define_resource_requirements requirements.assert(:rollback) do |a| a.assertion { all_releases[-2] } a.failure_message(RuntimeError, "There is no release to rollback to!") #There is no reason to assume 2 deployments in a single chef run, hence fails in whyrun. end [ @new_resource.before_migrate, @new_resource.before_symlink, @new_resource.before_restart, @new_resource.after_restart ].each do |script| requirements.assert(:deploy, :force_deploy) do |a| callback_file = "#{release_path}/#{script}" a.assertion do if script && script.class == String ::File.exist?(callback_file) else true end end a.failure_message(RuntimeError, "Can't find your callback file #{callback_file}") a.whyrun("Would assume callback file #{callback_file} included in release") end end end def action_deploy save_release_state if deployed?(release_path ) if current_release?(release_path ) Chef::Log.debug("#{@new_resource} is the latest version") else rollback_to release_path end else with_rollback_on_error do deploy end end end def action_force_deploy if deployed?(release_path) converge_by("delete deployed app at #{release_path} prior to force-deploy") do Chef::Log.info("Already deployed app at #{release_path}, forcing.") FileUtils.rm_rf(release_path) Chef::Log.info("#{@new_resource} forcing deploy of already deployed app at #{release_path}") end end # Alternatives: # * Move release_path directory before deploy and move it back when error occurs # * Rollback to previous commit # * Do nothing - because deploy is force, it will be retried in short time # Because last is simplest, keep it deploy end def action_rollback rollback_to all_releases[-2] end def rollback_to(target_release_path) @release_path = target_release_path rp_index = all_releases.index(release_path) releases_to_nuke = all_releases[(rp_index + 1)..-1] rollback releases_to_nuke.each do |i| converge_by("roll back by removing release #{i}") do Chef::Log.info "#{@new_resource} removing release: #{i}" FileUtils.rm_rf i end release_deleted(i) end end def deploy verify_directories_exist update_cached_repo # no converge-by - scm provider will dothis enforce_ownership copy_cached_repo install_gems enforce_ownership callback(:before_migrate, @new_resource.before_migrate) migrate callback(:before_symlink, @new_resource.before_symlink) symlink callback(:before_restart, @new_resource.before_restart) restart callback(:after_restart, @new_resource.after_restart) cleanup! Chef::Log.info "#{@new_resource} deployed to #{@new_resource.deploy_to}" end def rollback Chef::Log.info "#{@new_resource} rolling back to previous release #{release_path}" symlink Chef::Log.info "#{@new_resource} restarting with previous release" restart end def callback(what, callback_code=nil) @collection = Chef::ResourceCollection.new case callback_code when Proc Chef::Log.info "#{@new_resource} running callback #{what}" recipe_eval(&callback_code) when String run_callback_from_file("#{release_path}/#{callback_code}") when nil run_callback_from_file("#{release_path}/deploy/#{what}.rb") end end def migrate run_symlinks_before_migrate if @new_resource.migrate enforce_ownership environment = @new_resource.environment env_info = environment && environment.map do |key_and_val| "#{key_and_val.first}='#{key_and_val.last}'" end.join(" ") converge_by("execute migration command #{@new_resource.migration_command}") do Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}" run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :log_level => :info)) end end end def symlink purge_tempfiles_from_current_release link_tempfiles_to_current_release link_current_release_to_production Chef::Log.info "#{@new_resource} updated symlinks" end def restart if restart_cmd = @new_resource.restart_command if restart_cmd.kind_of?(Proc) Chef::Log.info("#{@new_resource} restarting app with embedded recipe") recipe_eval(&restart_cmd) else converge_by("restart app using command #{@new_resource.restart_command}") do Chef::Log.info("#{@new_resource} restarting app") run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path)) end end end end def cleanup! converge_by("update release history data") do release_created(release_path) end chop = -1 - @new_resource.keep_releases all_releases[0..chop].each do |old_release| converge_by("remove old release #{old_release}") do Chef::Log.info "#{@new_resource} removing old release #{old_release}" FileUtils.rm_rf(old_release) end release_deleted(old_release) end end def all_releases Dir.glob(Chef::Util::PathHelper.escape_glob(@new_resource.deploy_to) + "/releases/*").sort end def update_cached_repo if @new_resource.svn_force_export # TODO assertion, non-recoverable - @scm_provider must be svn if force_export? svn_force_export else run_scm_sync end end def run_scm_sync @scm_provider.run_action(:sync) end def svn_force_export Chef::Log.info "#{@new_resource} exporting source repository" @scm_provider.run_action(:force_export) end def copy_cached_repo target_dir_path = @new_resource.deploy_to + "/releases" converge_by("deploy from repo to #{target_dir_path} ") do FileUtils.rm_rf(release_path) if ::File.exist?(release_path) FileUtils.mkdir_p(target_dir_path) FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true) Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}" end end def enforce_ownership converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to) Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group end end def verify_directories_exist create_dir_unless_exists(@new_resource.deploy_to) create_dir_unless_exists(@new_resource.shared_path) end def link_current_release_to_production converge_by(["remove existing link at #{@new_resource.current_path}", "link release #{release_path} into production at #{@new_resource.current_path}"]) do FileUtils.rm_f(@new_resource.current_path) begin FileUtils.ln_sf(release_path, @new_resource.current_path) rescue => e raise Chef::Exceptions::FileNotFound.new("Cannot symlink current release to production: #{e.message}") end Chef::Log.info "#{@new_resource} linked release #{release_path} into production at #{@new_resource.current_path}" end enforce_ownership end def run_symlinks_before_migrate links_info = @new_resource.symlink_before_migrate.map { |src, dst| "#{src} => #{dst}" }.join(", ") converge_by("make pre-migration symlinks: #{links_info}") do @new_resource.symlink_before_migrate.each do |src, dest| begin FileUtils.ln_sf(@new_resource.shared_path + "/#{src}", release_path + "/#{dest}") rescue => e raise Chef::Exceptions::FileNotFound.new("Cannot symlink #{@new_resource.shared_path}/#{src} to #{release_path}/#{dest} before migrate: #{e.message}") end end Chef::Log.info "#{@new_resource} made pre-migration symlinks" end end def link_tempfiles_to_current_release dirs_info = @new_resource.create_dirs_before_symlink.join(",") @new_resource.create_dirs_before_symlink.each do |dir| create_dir_unless_exists(release_path + "/#{dir}") end Chef::Log.info("#{@new_resource} created directories before symlinking: #{dirs_info}") links_info = @new_resource.symlinks.map { |src, dst| "#{src} => #{dst}" }.join(", ") converge_by("link shared paths into current release: #{links_info}") do @new_resource.symlinks.each do |src, dest| begin FileUtils.ln_sf(::File.join(@new_resource.shared_path, src), ::File.join(release_path, dest)) rescue => e raise Chef::Exceptions::FileNotFound.new("Cannot symlink shared data #{::File.join(@new_resource.shared_path, src)} to #{::File.join(release_path, dest)}: #{e.message}") end end Chef::Log.info("#{@new_resource} linked shared paths into current release: #{links_info}") end run_symlinks_before_migrate enforce_ownership end def create_dirs_before_symlink end def purge_tempfiles_from_current_release log_info = @new_resource.purge_before_symlink.join(", ") converge_by("purge directories in checkout #{log_info}") do @new_resource.purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") } Chef::Log.info("#{@new_resource} purged directories in checkout #{log_info}") end end protected # Internal callback, called after copy_cached_repo. # Override if you need to keep state externally. # Note that YOU are responsible for implementing whyrun-friendly behavior # in any actions you take in this callback. def release_created(release_path) end # Note that YOU are responsible for using appropriate whyrun nomenclature # Override if you need to keep state externally. # Note that YOU are responsible for implementing whyrun-friendly behavior # in any actions you take in this callback. def release_deleted(release_path) end def release_slug raise Chef::Exceptions::Override, "You must override release_slug in #{self.to_s}" end def install_gems gem_resource_collection_runner.converge end def gem_resource_collection_runner gems_collection = Chef::ResourceCollection.new gem_packages.each { |rbgem| gems_collection.insert(rbgem) } gems_run_context = run_context.dup gems_run_context.resource_collection = gems_collection Chef::Runner.new(gems_run_context) end def gem_packages return [] unless ::File.exist?("#{release_path}/gems.yml") gems = YAML.load(IO.read("#{release_path}/gems.yml")) gems.map do |g| r = Chef::Resource::GemPackage.new(g[:name], run_context) r.version g[:version] r.action :install r.source "http://gems.github.com" r end end def run_options(run_opts={}) run_opts[:user] = @new_resource.user if @new_resource.user run_opts[:group] = @new_resource.group if @new_resource.group run_opts[:environment] = @new_resource.environment if @new_resource.environment run_opts[:log_tag] = @new_resource.to_s run_opts[:log_level] ||= :debug if run_opts[:log_level] == :info if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info? run_opts[:live_stream] = STDOUT end end run_opts end def run_callback_from_file(callback_file) Chef::Log.info "#{@new_resource} queueing checkdeploy hook #{callback_file}" recipe_eval do Dir.chdir(release_path) do from_file(callback_file) if ::File.exist?(callback_file) end end end def create_dir_unless_exists(dir) if ::File.directory?(dir) Chef::Log.debug "#{@new_resource} not creating #{dir} because it already exists" return false end converge_by("create new directory #{dir}") do begin FileUtils.mkdir_p(dir) Chef::Log.debug "#{@new_resource} created directory #{dir}" if @new_resource.user FileUtils.chown(@new_resource.user, nil, dir) Chef::Log.debug("#{@new_resource} set user to #{@new_resource.user} for #{dir}") end if @new_resource.group FileUtils.chown(nil, @new_resource.group, dir) Chef::Log.debug("#{@new_resource} set group to #{@new_resource.group} for #{dir}") end rescue => e raise Chef::Exceptions::FileNotFound.new("Cannot create directory #{dir}: #{e.message}") end end end def with_rollback_on_error yield rescue ::Exception => e if @new_resource.rollback_on_error Chef::Log.warn "Error on deploying #{release_path}: #{e.message}" failed_release = release_path if previous_release_path @release_path = previous_release_path rollback end converge_by("remove failed deploy #{failed_release}") do Chef::Log.info "Removing failed deploy #{failed_release}" FileUtils.rm_rf failed_release end release_deleted(failed_release) end raise end def save_release_state if ::File.exists?(@new_resource.current_path) release = ::File.readlink(@new_resource.current_path) @previous_release_path = release if ::File.exists?(release) end end def deployed?(release) all_releases.include?(release) end def current_release?(release) @previous_release_path == release end end end end chef-12.3.0/lib/chef/provider/cron/0000755000004100000410000000000012520074675017010 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/cron/solaris.rb0000644000004100000410000000160712520074675021015 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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/cron/unix" # Just to create an alias so 'Chef::Provider::Cron::Solaris' is exposed and accessible to existing consumers of class. Chef::Provider::Cron::Solaris = Chef::Provider::Cron::Unix chef-12.3.0/lib/chef/provider/cron/aix.rb0000644000004100000410000000324512520074675020122 0ustar www-datawww-data# # Author:: Kaustubh Deorukhkar () # 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/cron/unix" class Chef class Provider class Cron class Aix < Chef::Provider::Cron::Unix provides :cron, os: "aix" private # For AIX we ignore env vars/[ :mailto, :path, :shell, :home ] def get_crontab_entry if env_vars_are_set? raise Chef::Exceptions::Cron, "Aix cron entry does not support environment variables. Please set them in script and use script in cron." end newcron = "" newcron << "# Chef Name: #{new_resource.name}\n" newcron << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday}" newcron << " #{@new_resource.command}\n" newcron end def env_vars_are_set? @new_resource.environment.length > 0 || !@new_resource.mailto.nil? || !@new_resource.path.nil? || !@new_resource.shell.nil? || !@new_resource.home.nil? end end end end end chef-12.3.0/lib/chef/provider/cron/unix.rb0000644000004100000410000000535312520074675020326 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Author:: Toomas Pelberg (toomasp@gmx.net) # Copyright:: Copyright (c) 2009 Bryan McLellan # Copyright:: Copyright (c) 2010 Toomas Pelberg # 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/log' require 'chef/provider' class Chef class Provider class Cron class Unix < Chef::Provider::Cron include Chef::Mixin::ShellOut provides :cron, os: 'solaris2' private def read_crontab crontab = shell_out('/usr/bin/crontab -l', :user => @new_resource.user) status = crontab.status.exitstatus Chef::Log.debug crontab.format_for_exception if status > 0 if status > 1 raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status}" end return nil if status > 0 crontab.stdout.chomp << "\n" end def write_crontab(crontab) tempcron = Tempfile.new("chef-cron") tempcron << crontab tempcron.flush tempcron.chmod(0644) exit_status = 0 error_message = "" begin crontab_write = shell_out("/usr/bin/crontab #{tempcron.path}", :user => @new_resource.user) stderr = crontab_write.stderr exit_status = crontab_write.status.exitstatus # solaris9, 10 on some failures for example invalid 'mins' in crontab fails with exit code of zero :( if stderr && stderr.include?("errors detected in input, no crontab file generated") error_message = stderr exit_status = 1 end rescue Chef::Exceptions::Exec => e Chef::Log.debug(e.message) exit_status = 1 error_message = e.message rescue ArgumentError => e # usually raised on invalid user. Chef::Log.debug(e.message) exit_status = 1 error_message = e.message end tempcron.close! if exit_status > 0 raise Chef::Exceptions::Cron, "Error updating state of #{@new_resource.name}, exit: #{exit_status}, message: #{error_message}" end end end end end end chef-12.3.0/lib/chef/provider/resource_update.rb0000644000004100000410000000252612520074675021572 0ustar www-datawww-data class Chef class Provider # { # "run_id" : "1000", # "resource" : { # "type" : "file", # "name" : "/etc/passwd", # "start_time" : "2012-01-09T08:15:30-05:00", # "end_time" : "2012-01-09T08:15:30-05:00", # "status" : "modified", # "initial_state" : "exists", # "final_state" : "modified", # "before" : { # "group" : "root", # "owner" : "root", # "checksum" : "xyz" # }, # "after" : { # "group" : "root", # "owner" : "root", # "checksum" : "abc" # }, # "delta" : "escaped delta goes here" # }, # "event_data" : "" # } class ResourceUpdate attr_accessor :type attr_accessor :name attr_accessor :duration #ms attr_accessor :status attr_accessor :initial_state attr_accessor :final_state attr_accessor :initial_properties attr_accessor :final_properties attr_accessor :event_data # e.g., a diff. def initial_state_from_resource(resource) @initial_properties = resource.to_hash end def updated_state_from_resource(resource) @final_properties = resource.to_hash end end end end chef-12.3.0/lib/chef/provider/route.rb0000644000004100000410000001720612520074675017540 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org), Jesse Nelson (spheromak@gmail.com) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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/log' require 'chef/mixin/command' require 'chef/provider' require 'ipaddr' class Chef::Provider::Route < Chef::Provider include Chef::Mixin::Command provides :route attr_accessor :is_running MASK = {'0.0.0.0' => '0', '128.0.0.0' => '1', '192.0.0.0' => '2', '224.0.0.0' => '3', '240.0.0.0' => '4', '248.0.0.0' => '5', '252.0.0.0' => '6', '254.0.0.0' => '7', '255.0.0.0' => '8', '255.128.0.0' => '9', '255.192.0.0' => '10', '255.224.0.0' => '11', '255.240.0.0' => '12', '255.248.0.0' => '13', '255.252.0.0' => '14', '255.254.0.0' => '15', '255.255.0.0' => '16', '255.255.128.0' => '17', '255.255.192.0' => '18', '255.255.224.0' => '19', '255.255.240.0' => '20', '255.255.248.0' => '21', '255.255.252.0' => '22', '255.255.254.0' => '23', '255.255.255.0' => '24', '255.255.255.128' => '25', '255.255.255.192' => '26', '255.255.255.224' => '27', '255.255.255.240' => '28', '255.255.255.248' => '29', '255.255.255.252' => '30', '255.255.255.254' => '31', '255.255.255.255' => '32' } def hex2ip(hex_data) # Cleanup hex data hex_ip = hex_data.to_s.downcase.gsub(/[^0-9a-f]/, '') # Check hex data format (IP is a 32bit integer, so should be 8 chars long) return nil if hex_ip.length != hex_data.length || hex_ip.length != 8 # Extract octets from hex data octets = hex_ip.scan(/../).reverse.collect { |octet| [octet].pack('H2').unpack("C").first } # Validate IP ip = octets.join('.') begin IPAddr.new(ip, Socket::AF_INET).to_s rescue ArgumentError Chef::Log.debug("Invalid IP address data: hex=#{hex_ip}, ip=#{ip}") return nil end end def whyrun_supported? true end def load_current_resource self.is_running = false # cidr or quad dot mask if @new_resource.netmask new_ip = IPAddr.new("#{@new_resource.target}/#{@new_resource.netmask}") else new_ip = IPAddr.new(@new_resource.target) end # For linux, we use /proc/net/route file to read proc table info if node[:os] == "linux" route_file = ::File.open("/proc/net/route", "r") # Read all routes while (line = route_file.gets) # Get all the fields for a route iface,destination,gateway,flags,refcnt,use,metric,mask,mtu,window,irtt = line.split # Convert hex-encoded values to quad-dotted notation (e.g. 0064A8C0 => 192.168.100.0) destination = hex2ip(destination) gateway = hex2ip(gateway) mask = hex2ip(mask) # Skip formatting lines (header, etc) next unless destination && gateway && mask Chef::Log.debug("#{@new_resource} system has route: dest=#{destination} mask=#{mask} gw=#{gateway}") # check if what were trying to configure is already there # use an ipaddr object with ip/mask this way we can have # a new resource be in cidr format (i don't feel like # expanding bitmask by hand. # running_ip = IPAddr.new("#{destination}/#{mask}") Chef::Log.debug("#{@new_resource} new ip: #{new_ip.inspect} running ip: #{running_ip.inspect}") self.is_running = true if running_ip == new_ip && gateway == @new_resource.gateway end route_file.close end end def action_add # check to see if load_current_resource found the route if is_running Chef::Log.debug("#{@new_resource} route already active - nothing to do") else command = generate_command(:add) converge_by ("run #{ command } to add route") do run_command( :command => command ) Chef::Log.info("#{@new_resource} added") end end #for now we always write the file (ugly but its what it is) generate_config end def action_delete if is_running command = generate_command(:delete) converge_by ("run #{ command } to delete route ") do run_command( :command => command ) Chef::Log.info("#{@new_resource} removed") end else Chef::Log.debug("#{@new_resource} route does not exist - nothing to do") end #for now we always write the file (ugly but its what it is) generate_config end def generate_config conf = Hash.new case node[:platform] when "centos", "redhat", "fedora" # walk the collection run_context.resource_collection.each do |resource| if resource.is_a? Chef::Resource::Route # default to eth0 if resource.device dev = resource.device else dev = "eth0" end conf[dev] = String.new if conf[dev].nil? case @action when :add conf[dev] << config_file_contents(:add, :target => resource.target, :netmask => resource.netmask, :gateway => resource.gateway) when :delete # need to do this for the case when the last route on an int # is removed conf[dev] << config_file_contents(:delete) end end end conf.each do |k, v| network_file_name = "/etc/sysconfig/network-scripts/route-#{k}" converge_by ("write route route.#{k}\n#{conf[k]} to #{ network_file_name }") do network_file = ::File.new(network_file_name, "w") network_file.puts(conf[k]) Chef::Log.debug("#{@new_resource} writing route.#{k}\n#{conf[k]}") network_file.close end end end end def generate_command(action) common_route_items = '' common_route_items << "/#{MASK[@new_resource.netmask.to_s]}" if @new_resource.netmask common_route_items << " via #{@new_resource.gateway} " if @new_resource.gateway case action when :add command = "ip route replace #{@new_resource.target}" command << common_route_items command << " dev #{@new_resource.device} " if @new_resource.device when :delete command = "ip route delete #{@new_resource.target}" command << common_route_items end return command end def config_file_contents(action, options={}) content = '' case action when :add content << "#{options[:target]}" content << "/#{options[:netmask]}" if options[:netmask] content << " via #{options[:gateway]}" if options[:gateway] content << "\n" end return content end end chef-12.3.0/lib/chef/provider/package/0000755000004100000410000000000012520074675017442 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/package/windows/0000755000004100000410000000000012520074675021134 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/package/windows/msi.rb0000644000004100000410000000557512520074675022265 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'chef/mixin/shell_out' class Chef class Provider class Package class Windows class MSI include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/ include Chef::Mixin::ShellOut def initialize(resource) @new_resource = resource end # From Chef::Provider::Package def expand_options(options) options ? " #{options}" : "" end # Returns a version if the package is installed or nil if it is not. def installed_version Chef::Log.debug("#{@new_resource} getting product code for package at #{@new_resource.source}") product_code = get_product_property(@new_resource.source, "ProductCode") Chef::Log.debug("#{@new_resource} checking package status and version for #{product_code}") get_installed_version(product_code) end def package_version Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}") get_product_property(@new_resource.source, "ProductVersion") end def install_package(name, version) # We could use MsiConfigureProduct here, but we'll start off with msiexec Chef::Log.debug("#{@new_resource} installing MSI package '#{@new_resource.source}'") shell_out!("msiexec /qn /i \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns}) end def remove_package(name, version) # We could use MsiConfigureProduct here, but we'll start off with msiexec Chef::Log.debug("#{@new_resource} removing MSI package '#{@new_resource.source}'") shell_out!("msiexec /qn /x \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns}) end end end end end end chef-12.3.0/lib/chef/provider/package/ips.rb0000644000004100000410000000601312520074675020562 0ustar www-datawww-data# # Author:: Jason J. W. Williams () # Author:: Stephen Nelson-Smith () # 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 'open3' require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' class Chef class Provider class Package class Ips < Chef::Provider::Package provides :ips_package, os: "solaris2" attr_accessor :virtual def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { ! @candidate_version.nil? } a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.package_name} not found" a.whyrun "Assuming package #{@new_resource.package_name} would have been made available." end end def get_current_version shell_out("pkg info #{@new_resource.package_name}").stdout.each_line do |line| return $1.split[0] if line =~ /^\s+Version: (.*)/ end return nil end def get_candidate_version shell_out!("pkg info -r #{new_resource.package_name}").stdout.each_line do |line| return $1.split[0] if line =~ /Version: (.*)/ end return nil end def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) Chef::Log.debug("Checking package status for #{@new_resource.name}") @current_resource.version(get_current_version) @candidate_version = get_candidate_version @current_resource end def install_package(name, version) package_name = "#{name}@#{version}" normal_command = "pkg#{expand_options(@new_resource.options)} install -q #{package_name}" command = if @new_resource.respond_to?(:accept_license) and @new_resource.accept_license normal_command.gsub('-q', '-q --accept') else normal_command end shell_out(command) end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) package_name = "#{name}@#{version}" shell_out!( "pkg#{expand_options(@new_resource.options)} uninstall -q #{package_name}" ) end end end end end chef-12.3.0/lib/chef/provider/package/zypper.rb0000644000004100000410000001013112520074675021314 0ustar www-datawww-data# -*- coding: utf-8 -*- # # Authors:: Adam Jacob () # Ionuț Arțăriși () # Copyright:: Copyright (c) 2008 Opscode, Inc. # Copyright (c) 2013 SUSE Linux GmbH # 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/package' require 'chef/mixin/command' require 'chef/resource/package' require 'singleton' class Chef class Provider class Package class Zypper < Chef::Provider::Package def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) is_installed=false is_out_of_date=false version='' oud_version='' Chef::Log.debug("#{@new_resource} checking zypper") status = shell_out("zypper --non-interactive info #{@new_resource.package_name}") status.stdout.each_line do |line| case line when /^Version: (.+)$/ version = $1 Chef::Log.debug("#{@new_resource} version #{$1}") when /^Installed: Yes$/ is_installed=true Chef::Log.debug("#{@new_resource} is installed") when /^Installed: No$/ is_installed=false Chef::Log.debug("#{@new_resource} is not installed") when /^Status: out-of-date \(version (.+) installed\)$/ is_out_of_date=true oud_version=$1 Chef::Log.debug("#{@new_resource} out of date version #{$1}") end end if is_installed==false @candidate_version=version @current_resource.version(nil) end if is_installed==true if is_out_of_date==true @current_resource.version(oud_version) @candidate_version=version else @current_resource.version(version) @candidate_version=version end end unless status.exitstatus == 0 raise Chef::Exceptions::Package, "zypper failed - #{status.inspect}!" end @current_resource end def zypper_version() `zypper -V 2>&1`.scan(/\d+/).join(".").to_f end def install_package(name, version) zypper_package("install --auto-agree-with-licenses", name, version) end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) zypper_package("remove", name, version) end def purge_package(name, version) zypper_package("remove --clean-deps", name, version) end private def zypper_package(command, pkgname, version) version = "=#{version}" unless version.nil? || version.empty? if zypper_version < 1.0 shell_out!("zypper#{gpg_checks} #{command} -y #{pkgname}") else shell_out!("zypper --non-interactive#{gpg_checks} "+ "#{command} #{pkgname}#{version}") end end def gpg_checks() case Chef::Config[:zypper_check_gpg] when true "" when false " --no-gpg-checks" when nil Chef::Log.warn("Chef::Config[:zypper_check_gpg] was not set. " + "All packages will be installed without gpg signature checks. " + "This is a security hazard.") " --no-gpg-checks" end end end end end end chef-12.3.0/lib/chef/provider/package/openbsd.rb0000644000004100000410000001166312520074675021430 0ustar www-datawww-data# # Authors:: Bryan McLellan (btm@loftninjas.org) # Matthew Landauer (matthew@openaustralia.org) # Richard Manyanza (liseki@nyikacraftsmen.com) # Scott Bonds (scott@ggr.com) # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer # Copyright:: Copyright (c) 2014 Richard Manyanza, Scott Bonds # 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/package' require 'chef/provider/package' require 'chef/mixin/shell_out' require 'chef/mixin/get_source_from_package' require 'chef/exceptions' class Chef class Provider class Package class Openbsd < Chef::Provider::Package provides :package, os: "openbsd" include Chef::Mixin::ShellOut include Chef::Mixin::GetSourceFromPackage def initialize(*args) super @current_resource = Chef::Resource::Package.new(new_resource.name) end def load_current_resource @current_resource.package_name(new_resource.package_name) @current_resource.version(installed_version) @current_resource end def define_resource_requirements super # Below are incomplete/missing features for this package provider requirements.assert(:all_actions) do |a| a.assertion { !new_resource.source } a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support the source attribute') end requirements.assert(:all_actions) do |a| a.assertion do if new_resource.package_name =~ /^(.+?)--(.+)/ !new_resource.version else true end end a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support providing a version and flavor') end end def install_package(name, version) unless @current_resource.version if parts = name.match(/^(.+?)--(.+)/) # use double-dash for stems with flavors, see man page for pkg_add name = parts[1] end shell_out!("pkg_add -r #{name}#{version_string}", :env => {"PKG_PATH" => pkg_path}).status Chef::Log.debug("#{new_resource.package_name} installed") end end def remove_package(name, version) version_string = '' version_string += "-#{version}" if version if parts = name.match(/^(.+?)--(.+)/) name = parts[1] end shell_out!("pkg_delete #{name}#{version_string}", :env => nil).status end private def installed_version if parts = new_resource.package_name.match(/^(.+?)--(.+)/) name = parts[1] else name = new_resource.package_name end pkg_info = shell_out!("pkg_info -e \"#{name}->0\"", :env => nil, :returns => [0,1]) result = pkg_info.stdout[/^inst:#{Regexp.escape(name)}-(.+?)\s/, 1] Chef::Log.debug("installed_version of '#{new_resource.package_name}' is '#{result}'") result end def candidate_version @candidate_version ||= begin results = [] shell_out!("pkg_info -I \"#{new_resource.package_name}#{version_string}\"", :env => nil, :returns => [0,1]).stdout.each_line do |line| if parts = new_resource.package_name.match(/^(.+?)--(.+)/) results << line[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1] else results << line[/^#{Regexp.escape(new_resource.package_name)}-(.+?)\s/, 1] end end results = results.reject(&:nil?) Chef::Log.debug("candidate versions of '#{new_resource.package_name}' are '#{results}'") case results.length when 0 [] when 1 results[0] else raise Chef::Exceptions::Package, "#{new_resource.name} has multiple matching candidates. Please use a more specific name" if results.length > 1 end end end def version_string ver = '' ver += "-#{new_resource.version}" if new_resource.version end def pkg_path ENV['PKG_PATH'] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/" end end end end end chef-12.3.0/lib/chef/provider/package/freebsd/0000755000004100000410000000000012520074675021054 5ustar www-datawww-datachef-12.3.0/lib/chef/provider/package/freebsd/pkg.rb0000644000004100000410000000765612520074675022200 0ustar www-datawww-data# # Authors:: Bryan McLellan (btm@loftninjas.org) # Matthew Landauer (matthew@openaustralia.org) # Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer # Copyright:: Copyright (c) 2014 Richard Manyanza # 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/package/freebsd/base' require 'chef/util/path_helper' class Chef class Provider class Package module Freebsd class Pkg < Base include PortsHelper def install_package(name, version) unless @current_resource.version case @new_resource.source when /^http/, /^ftp/ if @new_resource.source =~ /\/$/ shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, 'LC_ALL' => nil }).status else shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGEROOT" => @new_resource.source, 'LC_ALL' => nil }).status end Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}") when /^\// shell_out!("pkg_add #{file_candidate_version_path}", :env => { "PKG_PATH" => @new_resource.source , 'LC_ALL'=>nil}).status Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}") else shell_out!("pkg_add -r #{latest_link_name}", :env => nil).status end end end def remove_package(name, version) shell_out!("pkg_delete #{package_name}-#{version || @current_resource.version}", :env => nil).status end # The name of the package (without the version number) as understood by pkg_add and pkg_info. def package_name if supports_ports? if makefile_variable_value("PKGNAME", port_path) =~ /^(.+)-[^-]+$/ $1 else raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile" end else @new_resource.package_name end end def latest_link_name makefile_variable_value("LATEST_LINK", port_path) end def current_installed_version pkg_info = shell_out!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0,1]) pkg_info.stdout[/^#{Regexp.escape(package_name)}-(.+)/, 1] end def candidate_version case @new_resource.source when /^http/, /^ftp/ repo_candidate_version when /^\// file_candidate_version else ports_candidate_version end end def file_candidate_version_path Dir[Chef::Util::PathHelper.escape_glob("#{@new_resource.source}/#{@current_resource.package_name}") + "*"][-1].to_s end def file_candidate_version file_candidate_version_path.split(/-/).last.split(/.tbz/).first end def repo_candidate_version "0.0.0" end def ports_candidate_version makefile_variable_value("PORTVERSION", port_path) end def port_path port_dir @new_resource.package_name end end end end end end chef-12.3.0/lib/chef/provider/package/freebsd/pkgng.rb0000644000004100000410000000532212520074675022511 0ustar www-datawww-data# # Authors:: Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2014 Richard Manyanza # 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/package/freebsd/base' class Chef class Provider class Package module Freebsd class Pkgng < Base def install_package(name, version) unless @current_resource.version case @new_resource.source when /^(http|ftp|\/)/ shell_out!("pkg add#{expand_options(@new_resource.options)} #{@new_resource.source}", :env => { 'LC_ALL' => nil }).status Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}") else shell_out!("pkg install -y#{expand_options(@new_resource.options)} #{name}", :env => { 'LC_ALL' => nil }).status end end end def remove_package(name, version) options = @new_resource.options && @new_resource.options.sub(repo_regex, '') options && !options.empty? || options = nil shell_out!("pkg delete -y#{expand_options(options)} #{name}#{version ? '-' + version : ''}", :env => nil).status end def current_installed_version pkg_info = shell_out!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70]) pkg_info.stdout[/^Version +: (.+)$/, 1] end def candidate_version @new_resource.source ? file_candidate_version : repo_candidate_version end private def file_candidate_version @new_resource.source[/#{Regexp.escape(@new_resource.package_name)}-(.+)\.txz/, 1] end def repo_candidate_version if @new_resource.options && @new_resource.options.match(repo_regex) options = $1 end pkg_query = shell_out!("pkg rquery#{expand_options(options)} '%v' #{@new_resource.package_name}", :env => nil) pkg_query.exitstatus.zero? ? pkg_query.stdout.strip.split(/\n/).last : nil end def repo_regex /(-r\s?\S+)\b/ end end end end end end chef-12.3.0/lib/chef/provider/package/freebsd/port.rb0000644000004100000410000000401212520074675022362 0ustar www-datawww-data# # Authors:: Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2014 Richard Manyanza # 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/package/freebsd/base' class Chef class Provider class Package module Freebsd class Port < Base include PortsHelper def install_package(name, version) shell_out!("make -DBATCH install clean", :timeout => 1800, :env => nil, :cwd => port_dir).status end def remove_package(name, version) shell_out!("make deinstall", :timeout => 300, :env => nil, :cwd => port_dir).status end def current_installed_version pkg_info = if @new_resource.supports_pkgng? shell_out!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70]) else shell_out!("pkg_info -E \"#{@new_resource.package_name}*\"", :env => nil, :returns => [0,1]) end pkg_info.stdout[/^#{Regexp.escape(@new_resource.package_name)}-(.+)/, 1] end def candidate_version if supports_ports? makefile_variable_value("PORTVERSION", port_dir) else raise Chef::Exceptions::Package, "Ports collection could not be found" end end def port_dir super(@new_resource.package_name) end end end end end end chef-12.3.0/lib/chef/provider/package/freebsd/base.rb0000644000004100000410000000611012520074675022311 0ustar www-datawww-data# # Authors:: Bryan McLellan (btm@loftninjas.org) # Matthew Landauer (matthew@openaustralia.org) # Richard Manyanza (liseki@nyikacraftsmen.com) # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer # Copyright:: Copyright (c) 2014 Richard Manyanza # 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/package' require 'chef/provider/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package module Freebsd module PortsHelper def supports_ports? ::File.exist?("/usr/ports/Makefile") end def port_dir(port) case port # When the package name starts with a '/' treat it as the full path to the ports directory. when /^\// port # Otherwise if the package name contains a '/' not at the start (like 'www/wordpress') treat # as a relative path from /usr/ports. when /\// "/usr/ports/#{port}" # Otherwise look up the path to the ports directory using 'whereis' else whereis = shell_out!("whereis -s #{port}", :env => nil) unless path = whereis.stdout[/^#{Regexp.escape(port)}:\s+(.+)$/, 1] raise Chef::Exceptions::Package, "Could not find port with the name #{port}" end path end end def makefile_variable_value(variable, dir = nil) options = dir ? { :cwd => dir } : {} make_v = shell_out!("make -V #{variable}", options.merge!(:env => nil, :returns => [0,1])) make_v.exitstatus.zero? ? make_v.stdout.strip.split($\).first : nil # $\ is the line separator, i.e. newline. end end class Base < Chef::Provider::Package include Chef::Mixin::GetSourceFromPackage def initialize(*args) super @current_resource = Chef::Resource::Package.new(@new_resource.name) end def load_current_resource @current_resource.package_name(@new_resource.package_name) @current_resource.version(current_installed_version) Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version @candidate_version = candidate_version Chef::Log.debug("#{@new_resource} candidate version is #{@candidate_version}") if @candidate_version @current_resource end end end end end end chef-12.3.0/lib/chef/provider/package/apt.rb0000644000004100000410000001664112520074675020563 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' class Chef class Provider class Package class Apt < Chef::Provider::Package provides :apt_package, os: "linux" # return [Hash] mapping of package name to Boolean value attr_accessor :is_virtual_package def initialize(new_resource, run_context) super @is_virtual_package = {} end def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) check_all_packages_state(@new_resource.package_name) @current_resource end def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { !@new_resource.source } a.failure_message(Chef::Exceptions::Package, 'apt package provider cannot handle source attribute. Use dpkg provider instead') end end def default_release_options # Use apt::Default-Release option only if provider supports it "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.respond_to?(:default_release) && @new_resource.default_release end def check_package_state(pkg) is_virtual_package = false installed = false installed_version = nil candidate_version = nil shell_out!("apt-cache#{expand_options(default_release_options)} policy #{pkg}", {:timeout=>900}).stdout.each_line do |line| case line when /^\s{2}Installed: (.+)$/ installed_version = $1 if installed_version == '(none)' Chef::Log.debug("#{@new_resource} current version is nil") installed_version = nil else Chef::Log.debug("#{@new_resource} current version is #{installed_version}") installed = true end when /^\s{2}Candidate: (.+)$/ candidate_version = $1 if candidate_version == '(none)' # This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm is_virtual_package = true showpkg = shell_out!("apt-cache showpkg #{pkg}", {:timeout => 900}).stdout providers = Hash.new showpkg.rpartition(/Reverse Provides: ?#{$/}/)[2].each_line do |line| provider, version = line.split providers[provider] = version end # Check if the package providing this virtual package is installed num_providers = providers.length raise Chef::Exceptions::Package, "#{@new_resource.package_name} has no candidate in the apt-cache" if num_providers == 0 # apt will only install a virtual package if there is a single providing package raise Chef::Exceptions::Package, "#{@new_resource.package_name} is a virtual package provided by #{num_providers} packages, you must explicitly select one to install" if num_providers > 1 # Check if the package providing this virtual package is installed Chef::Log.info("#{@new_resource} is a virtual package, actually acting on package[#{providers.keys.first}]") ret = check_package_state(providers.keys.first) installed = ret[:installed] installed_version = ret[:installed_version] else Chef::Log.debug("#{@new_resource} candidate version is #{$1}") end end end return { installed_version: installed_version, installed: installed, candidate_version: candidate_version, is_virtual_package: is_virtual_package, } end def check_all_packages_state(package) installed_version = {} candidate_version = {} installed = {} [package].flatten.each do |pkg| ret = check_package_state(pkg) is_virtual_package[pkg] = ret[:is_virtual_package] installed[pkg] = ret[:installed] installed_version[pkg] = ret[:installed_version] candidate_version[pkg] = ret[:candidate_version] end if package.is_a?(Array) @candidate_version = [] final_installed_version = [] [package].flatten.each do |pkg| @candidate_version << candidate_version[pkg] final_installed_version << installed_version[pkg] end @current_resource.version(final_installed_version) else @candidate_version = candidate_version[package] @current_resource.version(installed_version[package]) end end def install_package(name, version) name_array = [ name ].flatten version_array = [ version ].flatten package_name = name_array.zip(version_array).map do |n, v| is_virtual_package[n] ? n : "#{n}=#{v}" end.join(' ') run_noninteractive("apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}") end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) package_name = [ name ].flatten.join(' ') run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}") end def purge_package(name, version) package_name = [ name ].flatten.join(' ') run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} purge #{package_name}") end def preseed_package(preseed_file) Chef::Log.info("#{@new_resource} pre-seeding package installation instructions") run_noninteractive("debconf-set-selections #{preseed_file}") end def reconfig_package(name, version) package_name = [ name ].flatten.join(' ') Chef::Log.info("#{@new_resource} reconfiguring") run_noninteractive("dpkg-reconfigure #{package_name}") end private # Runs command via shell_out with magic environment to disable # interactive prompts. Command is run with default localization rather # than forcing locale to "C", so command output may not be stable. def run_noninteractive(command) shell_out!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @new_resource.timeout) end end end end end chef-12.3.0/lib/chef/provider/package/smartos.rb0000644000004100000410000000646312520074675021470 0ustar www-datawww-data# # Authors:: Trevor O (trevoro@joyent.com) # Bryan McLellan (btm@loftninjas.org) # Matthew Landauer (matthew@openaustralia.org) # Ben Rockwood (benr@joyent.com) # Copyright:: Copyright (c) 2009 Bryan McLellan, Matthew Landauer # 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/package' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class SmartOS < Chef::Provider::Package attr_accessor :is_virtual_package provides :smartos_package, os: "solaris2", platform_family: "smartos" def load_current_resource Chef::Log.debug("#{@new_resource} loading current resource") @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(nil) check_package_state(@new_resource.package_name) @current_resource # modified by check_package_state end def check_package_state(name) Chef::Log.debug("#{@new_resource} checking package #{name}") version = nil info = shell_out!("/opt/local/sbin/pkg_info -E \"#{name}*\"", :env => nil, :returns => [0,1]) if info.stdout version = info.stdout[/^#{@new_resource.package_name}-(.+)/, 1] end if !version @current_resource.version(nil) else @current_resource.version(version) end end def candidate_version return @candidate_version if @candidate_version name = nil version = nil pkg = shell_out!("/opt/local/bin/pkgin se #{new_resource.package_name}", :env => nil, :returns => [0,1]) pkg.stdout.each_line do |line| case line when /^#{new_resource.package_name}/ name, version = line.split[0].split(/-([^-]+)$/) end end @candidate_version = version version end def install_package(name, version) Chef::Log.debug("#{@new_resource} installing package #{name} version #{version}") package = "#{name}-#{version}" out = shell_out!("/opt/local/bin/pkgin -y install #{package}", :env => nil) end def upgrade_package(name, version) Chef::Log.debug("#{@new_resource} upgrading package #{name} version #{version}") install_package(name, version) end def remove_package(name, version) Chef::Log.debug("#{@new_resource} removing package #{name} version #{version}") package = "#{name}" out = shell_out!("/opt/local/bin/pkgin -y remove #{package}", :env => nil) end end end end end chef-12.3.0/lib/chef/provider/package/yum.rb0000644000004100000410000012647012520074675020613 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/config' require 'chef/provider/package' require 'chef/mixin/shell_out' require 'chef/resource/package' require 'singleton' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class Yum < Chef::Provider::Package provides :yum_package, os: "linux" class RPMUtils class << self # RPM::Version version_parse equivalent def version_parse(evr) return if evr.nil? epoch = nil # assume this is a version version = evr release = nil lead = 0 tail = evr.size if evr =~ %r{^([\d]+):} epoch = $1.to_i lead = $1.length + 1 elsif evr[0].ord == ":".ord epoch = 0 lead = 1 end if evr =~ %r{:?.*-(.*)$} release = $1 tail = evr.length - release.length - lead - 1 if release.empty? release = nil end end version = evr[lead,tail] if version.empty? version = nil end [ epoch, version, release ] end # verify def isalnum(x) isalpha(x) or isdigit(x) end def isalpha(x) v = x.ord (v >= 65 and v <= 90) or (v >= 97 and v <= 122) end def isdigit(x) v = x.ord v >= 48 and v <= 57 end # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0 def rpmvercmp(x, y) # easy! :) return 0 if x == y if x.nil? x = "" end if y.nil? y = "" end # not so easy :( # # takes 2 strings like # # x = "1.20.b18.el5" # y = "1.20.b17.el5" # # breaks into purely alpha and numeric segments and compares them using # some rules # # * 10 > 1 # * 1 > a # * z > a # * Z > A # * z > Z # * leading zeros are ignored # * separators (periods, commas) are ignored # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5" x_pos = 0 # overall string element reference position x_pos_max = x.length - 1 # number of elements in string, starting from 0 x_seg_pos = 0 # segment string element reference position x_comp = nil # segment to compare y_pos = 0 y_seg_pos = 0 y_pos_max = y.length - 1 y_comp = nil while (x_pos <= x_pos_max and y_pos <= y_pos_max) # first we skip over anything non alphanumeric while (x_pos <= x_pos_max) and (isalnum(x[x_pos]) == false) x_pos += 1 # +1 over pos_max if end of string end while (y_pos <= y_pos_max) and (isalnum(y[y_pos]) == false) y_pos += 1 end # if we hit the end of either we are done matching segments if (x_pos == x_pos_max + 1) or (y_pos == y_pos_max + 1) break end # we are now at the start of a alpha or numeric segment x_seg_pos = x_pos y_seg_pos = y_pos # grab segment so we can compare them if isdigit(x[x_seg_pos].ord) x_seg_is_num = true # already know it's a digit x_seg_pos += 1 # gather up our digits while (x_seg_pos <= x_pos_max) and isdigit(x[x_seg_pos]) x_seg_pos += 1 end # copy the segment but not the unmatched character that x_seg_pos will # refer to x_comp = x[x_pos,x_seg_pos - x_pos] while (y_seg_pos <= y_pos_max) and isdigit(y[y_seg_pos]) y_seg_pos += 1 end y_comp = y[y_pos,y_seg_pos - y_pos] else # we are comparing strings x_seg_is_num = false while (x_seg_pos <= x_pos_max) and isalpha(x[x_seg_pos]) x_seg_pos += 1 end x_comp = x[x_pos,x_seg_pos - x_pos] while (y_seg_pos <= y_pos_max) and isalpha(y[y_seg_pos]) y_seg_pos += 1 end y_comp = y[y_pos,y_seg_pos - y_pos] end # if y_seg_pos didn't advance in the above loop it means the segments are # different types if y_pos == y_seg_pos # numbers always win over letters return x_seg_is_num ? 1 : -1 end # move the ball forward before we mess with the segments x_pos += x_comp.length # +1 over pos_max if end of string y_pos += y_comp.length # we are comparing numbers - simply convert them if x_seg_is_num x_comp = x_comp.to_i y_comp = y_comp.to_i end # compares ints or strings # don't return if equal - try the next segment if x_comp > y_comp return 1 elsif x_comp < y_comp return -1 end # if we've reached here than the segments are the same - try again end # we must have reached the end of one or both of the strings and they # matched up until this point # segments matched completely but the segment separators were different - # rpm reference code treats these as equal. if (x_pos == x_pos_max + 1) and (y_pos == y_pos_max + 1) return 0 end # the most unprocessed characters left wins if (x_pos_max - x_pos) > (y_pos_max - y_pos) return 1 else return -1 end end end # self end # RPMUtils class RPMVersion include Comparable def initialize(*args) if args.size == 1 @e, @v, @r = RPMUtils.version_parse(args[0]) elsif args.size == 3 @e = args[0].to_i @v = args[1] @r = args[2] else raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " + "version, release'" end end attr_reader :e, :v, :r alias :epoch :e alias :version :v alias :release :r def self.parse(*args) self.new(*args) end def <=>(y) compare_versions(y) end def compare(y) compare_versions(y, false) end def partial_compare(y) compare_versions(y, true) end # RPM::Version rpm_version_to_s equivalent def to_s if @r.nil? @v else "#{@v}-#{@r}" end end def evr "#{@e}:#{@v}-#{@r}" end private # Rough RPM::Version rpm_version_cmp equivalent - except much slower :) # # partial lets epoch and version segment equality be good enough to return equal, eg: # # 2:1.2-1 == 2:1.2 # 2:1.2-1 == 2: # def compare_versions(y, partial=false) x = self # compare epoch if (x.e.nil? == false and x.e > 0) and y.e.nil? return 1 elsif x.e.nil? and (y.e.nil? == false and y.e > 0) return -1 elsif x.e.nil? == false and y.e.nil? == false if x.e < y.e return -1 elsif x.e > y.e return 1 end end # compare version if partial and (x.v.nil? or y.v.nil?) return 0 elsif x.v.nil? == false and y.v.nil? return 1 elsif x.v.nil? and y.v.nil? == false return -1 elsif x.v.nil? == false and y.v.nil? == false cmp = RPMUtils.rpmvercmp(x.v, y.v) return cmp if cmp != 0 end # compare release if partial and (x.r.nil? or y.r.nil?) return 0 elsif x.r.nil? == false and y.r.nil? return 1 elsif x.r.nil? and y.r.nil? == false return -1 elsif x.r.nil? == false and y.r.nil? == false cmp = RPMUtils.rpmvercmp(x.r, y.r) return cmp end return 0 end end class RPMPackage include Comparable def initialize(*args) if args.size == 4 @n = args[0] @version = RPMVersion.new(args[1]) @a = args[2] @provides = args[3] elsif args.size == 6 @n = args[0] e = args[1].to_i v = args[2] r = args[3] @version = RPMVersion.new(e,v,r) @a = args[4] @provides = args[5] else raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " + "or 'name, epoch, version, release, arch, provides'" end # We always have one, ourselves! if @provides.empty? @provides = [ RPMProvide.new(@n, @version.evr, :==) ] end end attr_reader :n, :a, :version, :provides alias :name :n alias :arch :a def <=>(y) compare(y) end def compare(y) x = self # easy! :) return 0 if x.nevra == y.nevra # compare name if x.n.nil? == false and y.n.nil? return 1 elsif x.n.nil? and y.n.nil? == false return -1 elsif x.n.nil? == false and y.n.nil? == false if x.n < y.n return -1 elsif x.n > y.n return 1 end end # compare version if x.version > y.version return 1 elsif x.version < y.version return -1 end # compare arch if x.a.nil? == false and y.a.nil? return 1 elsif x.a.nil? and y.a.nil? == false return -1 elsif x.a.nil? == false and y.a.nil? == false if x.a < y.a return -1 elsif x.a > y.a return 1 end end return 0 end def to_s nevra end def nevra "#{@n}-#{@version.evr}.#{@a}" end end # Simple implementation from rpm and ruby-rpm reference code class RPMDependency def initialize(*args) if args.size == 3 @name = args[0] @version = RPMVersion.new(args[1]) # Our requirement to other dependencies @flag = args[2] || :== elsif args.size == 5 @name = args[0] e = args[1].to_i v = args[2] r = args[3] @version = RPMVersion.new(e,v,r) @flag = args[4] || :== else raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " + "'name, epoch, version, release, flag'" end end attr_reader :name, :version, :flag # Parses 2 forms: # # "mtr >= 2:0.71-3.0" # "mta" def self.parse(string) if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$} name = $1 if $2 == "=" flag = :== else flag = :"#{$2}" end version = $3 return self.new(name, version, flag) else name = string return self.new(name, nil, nil) end end # Test if another RPMDependency satisfies our requirements def satisfy?(y) unless y.kind_of?(RPMDependency) raise ArgumentError, "Expecting an RPMDependency object" end x = self # Easy! if x.name != y.name return false end # Partial compare # # eg: x.version 2.3 == y.version 2.3-1 sense = x.version.partial_compare(y.version) # Thanks to rpmdsCompare() rpmds.c if sense < 0 and (x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<) return true elsif sense > 0 and (x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>) return true elsif sense == 0 and ( ((x.flag == :== or x.flag == :<= or x.flag == :>=) and (y.flag == :== or y.flag == :<= or y.flag == :>=)) or (x.flag == :< and y.flag == :<) or (x.flag == :> and y.flag == :>) ) return true end return false end end class RPMProvide < RPMDependency; end class RPMRequire < RPMDependency; end class RPMDbPackage < RPMPackage # , installed, available def initialize(*args) @repoid = args.pop # state @available = args.pop @installed = args.pop super(*args) end attr_reader :repoid, :available, :installed end # Simple storage for RPMPackage objects - keeps them unique and sorted class RPMDb def initialize # package name => [ RPMPackage, RPMPackage ] of different versions @rpms = Hash.new # package nevra => RPMPackage for lookups @index = Hash.new # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature @provides = Hash.new # RPMPackages listed as available @available = Set.new # RPMPackages listed as installed @installed = Set.new end def [](package_name) self.lookup(package_name) end # Lookup package_name and return a descending array of package objects def lookup(package_name) pkgs = @rpms[package_name] if pkgs return pkgs.sort.reverse else return nil end end def lookup_provides(provide_name) @provides[provide_name] end # Using the package name as a key, and nevra for an index, keep a unique list of packages. # The available/installed state can be overwritten for existing packages. def push(*args) args.flatten.each do |new_rpm| unless new_rpm.kind_of?(RPMDbPackage) raise ArgumentError, "Expecting an RPMDbPackage object" end @rpms[new_rpm.n] ||= Array.new # we may already have this one, like when the installed list is refreshed idx = @index[new_rpm.nevra] if idx # grab the existing package if it's not curr_rpm = idx else @rpms[new_rpm.n] << new_rpm new_rpm.provides.each do |provide| @provides[provide.name] ||= Array.new @provides[provide.name] << new_rpm end curr_rpm = new_rpm end # Track the nevra -> RPMPackage association to avoid having to compare versions # with @rpms[new_rpm.n] on the next round @index[new_rpm.nevra] = curr_rpm # these are overwritten for existing packages if new_rpm.available @available << curr_rpm end if new_rpm.installed @installed << curr_rpm end end end def <<(*args) self.push(args) end def clear @rpms.clear @index.clear @provides.clear clear_available clear_installed end def clear_available @available.clear end def clear_installed @installed.clear end def size @rpms.size end alias :length :size def available_size @available.size end def installed_size @installed.size end def available?(package) @available.include?(package) end def installed?(package) @installed.include?(package) end def whatprovides(rpmdep) unless rpmdep.kind_of?(RPMDependency) raise ArgumentError, "Expecting an RPMDependency object" end what = [] packages = lookup_provides(rpmdep.name) if packages packages.each do |pkg| pkg.provides.each do |provide| if provide.satisfy?(rpmdep) what << pkg end end end end return what end end # Cache for our installed and available packages, pulled in from yum-dump.py class YumCache include Chef::Mixin::Command include Chef::Mixin::ShellOut include Singleton def initialize @rpmdb = RPMDb.new # Next time @rpmdb is accessed: # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates # yum's cache and parses options from /etc/yum.conf. Pulls in Provides # dependency data for installed packages only - this data is slow to # gather. # :provides - Same as :all but pulls in Provides data for available packages as well. # Used as a last resort when we can't find a Provides match. # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm # db. Used between client runs for a quick refresh. # :none - Do nothing, a call to one of the reload methods is required. @next_refresh = :all @allow_multi_install = [] @extra_repo_control = nil # these are for subsequent runs if we are on an interval Chef::Client.when_run_starts do YumCache.instance.reload end end attr_reader :extra_repo_control # Cache management # def refresh case @next_refresh when :none return nil when :installed reset_installed # fast opts=" --installed" when :all reset # medium opts=" --options --installed-provides" when :provides reset # slow! opts=" --options --all-provides" else raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}" end if @extra_repo_control opts << " #{@extra_repo_control}" end opts << " --yum-lock-timeout #{Chef::Config[:yum_lock_timeout]}" one_line = false error = nil helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py') status = nil begin status = shell_out!("/usr/bin/python #{helper}#{opts}", :timeout => Chef::Config[:yum_timeout]) status.stdout.each_line do |line| one_line = true line.chomp! if line =~ %r{\[option (.*)\] (.*)} if $1 == "installonlypkgs" @allow_multi_install = $2.split else raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py" end next end if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r]) (\S+)$} name = $1 epoch = $2 version = $3 release = $4 arch = $5 provides = parse_provides($6) type = $7 repoid = $8 else Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " + "Please check your yum configuration.") next end case type when "i" # if yum-dump was called with --installed this may not be true, but it's okay # since we don't touch the @available Set in reload_installed available = false installed = true when "a" available = true installed = false when "r" available = true installed = true end pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available, repoid) @rpmdb << pkg end error = status.stderr rescue Mixlib::ShellOut::CommandTimeout => e Chef::Log.error("#{helper} exceeded timeout #{Chef::Config[:yum_timeout]}") raise(e) end if status.exitstatus != 0 raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}" else unless one_line Chef::Log.warn("Odd, no output from yum-dump.py. Please check " + "your yum configuration.") end end # A reload method must be called before the cache is altered @next_refresh = :none end def reload @next_refresh = :all end def reload_installed @next_refresh = :installed end def reload_provides @next_refresh = :provides end def reset @rpmdb.clear end def reset_installed @rpmdb.clear_installed end # Querying the cache # # Check for package by name or name+arch def package_available?(package_name) refresh if @rpmdb.lookup(package_name) return true else if package_name =~ %r{^(.*)\.(.*)$} pkg_name = $1 pkg_arch = $2 if matches = @rpmdb.lookup(pkg_name) matches.each do |m| return true if m.arch == pkg_arch end end end end return false end # Returns a array of packages satisfying an RPMDependency def packages_from_require(rpmdep) refresh @rpmdb.whatprovides(rpmdep) end # Check if a package-version.arch is available to install def version_available?(package_name, desired_version, arch=nil) version(package_name, arch, true, false) do |v| return true if desired_version == v end return false end # Return the source repository for a package-version.arch def package_repository(package_name, desired_version, arch=nil) package(package_name, arch, true, false) do |pkg| return pkg.repoid if desired_version == pkg.version.to_s end return nil end # Return the latest available version for a package.arch def available_version(package_name, arch=nil) version(package_name, arch, true, false) end alias :candidate_version :available_version # Return the currently installed version for a package.arch def installed_version(package_name, arch=nil) version(package_name, arch, false, true) end # Return an array of packages allowed to be installed multiple times, such as the kernel def allow_multi_install refresh @allow_multi_install end def enable_extra_repo_control(arg) # Don't touch cache if it's the same repos as the last load unless @extra_repo_control == arg @extra_repo_control = arg reload end end def disable_extra_repo_control # Only force reload when set if @extra_repo_control @extra_repo_control = nil reload end end private def version(package_name, arch=nil, is_available=false, is_installed=false) package(package_name, arch, is_available, is_installed) do |pkg| if block_given? yield pkg.version.to_s else # first match is latest version return pkg.version.to_s end end if block_given? return self else return nil end end def package(package_name, arch=nil, is_available=false, is_installed=false) refresh packages = @rpmdb[package_name] if packages packages.each do |pkg| if is_available next unless @rpmdb.available?(pkg) end if is_installed next unless @rpmdb.installed?(pkg) end if arch next unless pkg.arch == arch end if block_given? yield pkg else # first match is latest version return pkg end end end if block_given? return self else return nil end end # Parse provides from yum-dump.py output def parse_provides(string) ret = [] # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0'] string.split(", ").each do |seg| # 'atk = 1.12.2-1.fc6' if seg =~ %r{^'(.*)'$} ret << RPMProvide.parse($1) end end return ret end end # YumCache include Chef::Mixin::GetSourceFromPackage def initialize(new_resource, run_context) super @yum = YumCache.instance end # Extra attributes # def arch if @new_resource.respond_to?("arch") @new_resource.arch else nil end end def flush_cache if @new_resource.respond_to?("flush_cache") @new_resource.flush_cache else { :before => false, :after => false } end end # Helpers # def yum_arch arch ? ".#{arch}" : nil end def yum_command(command) status = shell_out(command, {:timeout => Chef::Config[:yum_timeout]}) # This is fun: rpm can encounter errors in the %post/%postun scripts which aren't # considered fatal - meaning the rpm is still successfully installed. These issue # cause yum to emit a non fatal warning but still exit(1). As there's currently no # way to suppress this behavior and an exit(1) will break a Chef run we make an # effort to trap these and re-run the same install command - it will either fail a # second time or succeed. # # A cleaner solution would have to be done in python and better hook into # yum/rpm to handle exceptions as we see fit. if status.exitstatus == 1 status.stdout.each_line do |l| # rpm-4.4.2.3 lib/psm.c line 2182 if l =~ %r{^error: %(post|postun)\(.*\) scriptlet failed, exit status \d+$} Chef::Log.warn("#{@new_resource} caught non-fatal scriptlet issue: \"#{l}\". Can't trust yum exit status " + "so running install again to verify.") status = shell_out(command, {:timeout => Chef::Config[:yum_timeout]}) break end end end if status.exitstatus > 0 command_output = "STDOUT: #{status.stdout}\nSTDERR: #{status.stderr}" raise Chef::Exceptions::Exec, "#{command} returned #{status.exitstatus}:\n#{command_output}" end end # Standard Provider methods for Parent # def load_current_resource if flush_cache[:before] @yum.reload end if @new_resource.options repo_control = [] @new_resource.options.split.each do |opt| if opt =~ %r{--(enable|disable)repo=.+} repo_control << opt end end if repo_control.size > 0 @yum.enable_extra_repo_control(repo_control.join(" ")) else @yum.disable_extra_repo_control end else @yum.disable_extra_repo_control end # At this point package_name could be: # # 1) a package name, eg: "foo" # 2) a package name.arch, eg: "foo.i386" # 3) or a dependency, eg: "foo >= 1.1" # Check if we have name or name+arch which has a priority over a dependency package_name_array.each_with_index do |n, index| unless @yum.package_available?(n) # If they aren't in the installed packages they could be a dependency dep = parse_dependency(n, new_version_array[index]) if dep if @new_resource.package_name.is_a?(Array) @new_resource.package_name(package_name_array - [n] + [dep.first]) @new_resource.version(new_version_array - [new_version_array[index]] + [dep.last]) if dep.last else @new_resource.package_name(dep.first) @new_resource.version(dep.last) if dep.last end end end end # Don't overwrite an existing arch unless arch parse_arch end @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) installed_version = [] @candidate_version = [] if @new_resource.source unless ::File.exists?(@new_resource.source) raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}" end Chef::Log.debug("#{@new_resource} checking rpm status") shell_out!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}", :timeout => Chef::Config[:yum_timeout]).stdout.each_line do |line| case line when /([\w\d_.-]+)\s([\w\d_.-]+)/ @current_resource.package_name($1) @new_resource.version($2) end end @candidate_version << @new_resource.version installed_version << @yum.installed_version(@current_resource.package_name, arch) else if @new_resource.version new_resource = "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch}" else new_resource = "#{@new_resource.package_name}#{yum_arch}" end Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}") package_name_array.each do |pkg| installed_version << @yum.installed_version(pkg, arch) @candidate_version << @yum.candidate_version(pkg, arch) end end if installed_version.size == 1 @current_resource.version(installed_version[0]) @candidate_version = @candidate_version[0] else @current_resource.version(installed_version) end Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " + "#{@candidate_version || "(none)"}") @current_resource end def install_remote_package(name, version) # Work around yum not exiting with an error if a package doesn't exist # for CHEF-2062 all_avail = as_array(name).zip(as_array(version)).any? do |n, v| @yum.version_available?(n, v, arch) end method = log_method = nil methods = [] if all_avail # More Yum fun: # # yum install of an old name+version will exit(1) # yum install of an old name+version+arch will exit(0) for some reason # # Some packages can be installed multiple times like the kernel as_array(name).zip(as_array(version)).each do |n, v| method = "install" log_method = "installing" idx = package_name_array.index(n) unless @yum.allow_multi_install.include?(n) if RPMVersion.parse(current_version_array[idx]) > RPMVersion.parse(v) # We allow downgrading only in the evenit of single-package # rules where the user explicitly allowed it if allow_downgrade method = "downgrade" log_method = "downgrading" else # we bail like yum when the package is older raise Chef::Exceptions::Package, "Installed package #{n}-#{current_version_array[idx]} is newer " + "than candidate package #{n}-#{v}" end end end # methods don't count for packages we won't be touching next if RPMVersion.parse(current_version_array[idx]) == RPMVersion.parse(v) methods << method end # We could split this up into two commands if we wanted to, but # for now, just don't support this. if methods.uniq.length > 1 raise Chef::Exceptions::Package, "Multipackage rule #{name} has a mix of upgrade and downgrade packages. Cannot proceed." end repos = [] pkg_string_bits = [] index = 0 as_array(name).zip(as_array(version)).each do |n, v| s = '' unless v == current_version_array[index] s = "#{n}-#{v}#{yum_arch}" repo = @yum.package_repository(n, v, arch) repos << "#{s} from #{repo} repository" pkg_string_bits << s end index += 1 end pkg_string = pkg_string_bits.join(' ') Chef::Log.info("#{@new_resource} #{log_method} #{repos.join(' ')}") yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{pkg_string}") else raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " + "and release? (version-release, e.g. 1.84-10.fc6)" end end def install_package(name, version) if @new_resource.source yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}") else install_remote_package(name, version) end if flush_cache[:after] @yum.reload else @yum.reload_installed end end # Keep upgrades from trying to install an older candidate version. Can happen when a new # version is installed then removed from a repository, now the older available version # shows up as a viable install candidate. # # Can be done in upgrade_package but an upgraded from->to log message slips out # # Hacky - better overall solution? Custom compare in Package provider? def action_upgrade # Could be uninstalled or have no candidate if @current_resource.version.nil? || !candidate_version_array.any? super elsif candidate_version_array.zip(current_version_array).any? do |c, i| RPMVersion.parse(c) > RPMVersion.parse(i) end super else Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do") end end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) if version remove_str = as_array(name).zip(as_array(version)).map do |x| "#{x.join('-')}#{yum_arch}" end.join(' ') else remove_str = as_array(name).map { |n| "#{n}#{yum_arch}" }.join(' ') end yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}") if flush_cache[:after] @yum.reload else @yum.reload_installed end end def purge_package(name, version) remove_package(name, version) end private def parse_arch # Allow for foo.x86_64 style package_name like yum uses in it's output # if @new_resource.package_name =~ %r{^(.*)\.(.*)$} new_package_name = $1 new_arch = $2 # foo.i386 and foo.beta1 are both valid package names or expressions of an arch. # Ensure we don't have an existing package matching package_name, then ensure we at # least have a match for the new_package+new_arch before we overwrite. If neither # then fall through to standard package handling. if (@yum.installed_version(@new_resource.package_name).nil? and @yum.candidate_version(@new_resource.package_name).nil?) and (@yum.installed_version(new_package_name, new_arch) or @yum.candidate_version(new_package_name, new_arch)) @new_resource.package_name(new_package_name) @new_resource.arch(new_arch) end end end # If we don't have the package we could have been passed a 'whatprovides' feature # # eg: yum install "perl(Config)" # yum install "mtr = 2:0.71-3.1" # yum install "mtr > 2:0.71" # # We support resolving these out of the Provides data imported from yum-dump.py and # matching them up with an actual package so the standard resource handling can apply. # # There is currently no support for filename matching. def parse_dependency(name,version) # Transform the package_name into a requirement # If we are passed a version or a version constraint we have to assume it's a requirement first. If it can't be # parsed only yum_require.name will be set and @new_resource.version will be left intact if version require_string = "#{name} #{version}" else # Transform the package_name into a requirement, might contain a version, could just be # a match for virtual provides require_string = name end yum_require = RPMRequire.parse(require_string) # and gather all the packages that have a Provides feature satisfying the requirement. # It could be multiple be we can only manage one packages = @yum.packages_from_require(yum_require) if packages.empty? # Don't bother if we are just ensuring a package is removed - we don't need Provides data actions = Array(@new_resource.action) unless actions.size == 1 and (actions[0] == :remove || actions[0] == :purge) Chef::Log.debug("#{@new_resource} couldn't match #{@new_resource.package_name} in " + "installed Provides, loading available Provides - this may take a moment") @yum.reload_provides packages = @yum.packages_from_require(yum_require) end end unless packages.empty? new_package_name = packages.first.name new_package_version = packages.first.version.to_s debug_msg = "#{name}: Unable to match package '#{name}' but matched #{packages.size} " debug_msg << packages.size == 1 ? "package" : "packages" debug_msg << ", selected '#{new_package_name}' version '#{new_package_version}'" Chef::Log.debug(debug_msg) # Ensure it's not the same package under a different architecture unique_names = [] packages.each do |pkg| unique_names << "#{pkg.name}-#{pkg.version.evr}" end unique_names.uniq! if unique_names.size > 1 Chef::Log.warn("#{@new_resource} matched multiple Provides for #{@new_resource.package_name} " + "but we can only use the first match: #{new_package_name}. Please use a more " + "specific version.") end if yum_require.version.to_s.nil? new_package_version = nil end [new_package_name,new_package_version] end end end end end end chef-12.3.0/lib/chef/provider/package/yum-dump.py0000644000004100000410000002300012520074675021564 0ustar www-datawww-data# # Author:: Matthew Kent () # Copyright:: Copyright (c) 2009, 2011 Matthew Kent # 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. # # yum-dump.py # Inspired by yumhelper.py by David Lutterkort # # Produce a list of installed, available and re-installable packages using yum # and dump the results to stdout. # # yum-dump invokes yum similarly to the command line interface which makes it # subject to most of the configuration parameters in yum.conf. yum-dump will # also load yum plugins in the same manor as yum - these can affect the output. # # Can be run as non root, but that won't update the cache. # # Intended to support yum 2.x and 3.x import os import sys import time import yum import re import errno from yum import Errors from optparse import OptionParser from distutils import version YUM_PID_FILE='/var/run/yum.pid' YUM_VER = version.StrictVersion(yum.__version__) YUM_MAJOR = YUM_VER.version[0] if YUM_MAJOR > 3 or YUM_MAJOR < 2: print >> sys.stderr, "yum-dump Error: Can't match supported yum version" \ " (%s)" % yum.__version__ sys.exit(1) # Required for Provides output if YUM_MAJOR == 2: import rpm import rpmUtils.miscutils def setup(yb, options): # Only want our output # if YUM_MAJOR == 3: try: if YUM_VER >= version.StrictVersion("3.2.22"): yb.preconf.errorlevel=0 yb.preconf.debuglevel=0 # initialize the config yb.conf else: yb.doConfigSetup(errorlevel=0, debuglevel=0) except yum.Errors.ConfigError, e: # suppresses an ignored exception at exit yb.preconf = None print >> sys.stderr, "yum-dump Config Error: %s" % e return 1 except ValueError, e: yb.preconf = None print >> sys.stderr, "yum-dump Options Error: %s" % e return 1 elif YUM_MAJOR == 2: yb.doConfigSetup() def __log(a,b): pass yb.log = __log yb.errorlog = __log # Give Chef every possible package version, it can decide what to do with them if YUM_MAJOR == 3: yb.conf.showdupesfromrepos = True elif YUM_MAJOR == 2: yb.conf.setConfigOption('showdupesfromrepos', True) # Optionally run only on cached repositories, but non root must use the cache if os.geteuid() != 0: if YUM_MAJOR == 3: yb.conf.cache = True elif YUM_MAJOR == 2: yb.conf.setConfigOption('cache', True) else: if YUM_MAJOR == 3: yb.conf.cache = options.cache elif YUM_MAJOR == 2: yb.conf.setConfigOption('cache', options.cache) # Handle repo toggle via id or glob exactly like yum for opt, repos in options.repo_control: for repo in repos: if opt == '--enablerepo': yb.repos.enableRepo(repo) elif opt == '--disablerepo': yb.repos.disableRepo(repo) return 0 def dump_packages(yb, list, output_provides): packages = {} if YUM_MAJOR == 2: yb.doTsSetup() yb.doRepoSetup() yb.doSackSetup() db = yb.doPackageLists(list) for pkg in db.installed: pkg.type = 'i' packages[str(pkg)] = pkg if YUM_VER >= version.StrictVersion("3.2.21"): for pkg in db.available: pkg.type = 'a' packages[str(pkg)] = pkg # These are both installed and available for pkg in db.reinstall_available: pkg.type = 'r' packages[str(pkg)] = pkg else: # Old style method - no reinstall list for pkg in yb.pkgSack.returnPackages(): if str(pkg) in packages: if packages[str(pkg)].type == "i": packages[str(pkg)].type = 'r' continue pkg.type = 'a' packages[str(pkg)] = pkg unique_packages = packages.values() unique_packages.sort(lambda x, y: cmp(x.name, y.name)) for pkg in unique_packages: if output_provides == "all" or \ (output_provides == "installed" and (pkg.type == "i" or pkg.type == "r")): # yum 2 doesn't have provides_print, implement it ourselves using methods # based on requires gathering in packages.py if YUM_MAJOR == 2: provlist = [] # Installed and available are gathered in different ways if pkg.type == 'i' or pkg.type == 'r': names = pkg.hdr[rpm.RPMTAG_PROVIDENAME] flags = pkg.hdr[rpm.RPMTAG_PROVIDEFLAGS] ver = pkg.hdr[rpm.RPMTAG_PROVIDEVERSION] if names is not None: tmplst = zip(names, flags, ver) for (n, f, v) in tmplst: prov = rpmUtils.miscutils.formatRequire(n, v, f) provlist.append(prov) # This is slow :( elif pkg.type == 'a': for prcoTuple in pkg.returnPrco('provides'): prcostr = pkg.prcoPrintable(prcoTuple) provlist.append(prcostr) provides = provlist else: provides = pkg.provides_print else: provides = "[]" print '%s %s %s %s %s %s %s %s' % ( pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch, provides, pkg.type, pkg.repoid ) return 0 def yum_dump(options): lock_obtained = False yb = yum.YumBase() status = setup(yb, options) if status != 0: return status if options.output_options: print "[option installonlypkgs] %s" % " ".join(yb.conf.installonlypkgs) # Non root can't handle locking on rhel/centos 4 if os.geteuid() != 0: return dump_packages(yb, options.package_list, options.output_provides) # Wrap the collection and output of packages in yum's global lock to prevent # any inconsistencies. try: # Spin up to --yum-lock-timeout option countdown = options.yum_lock_timeout while True: try: yb.doLock(YUM_PID_FILE) lock_obtained = True except Errors.LockError, e: time.sleep(1) countdown -= 1 if countdown == 0: print >> sys.stderr, "yum-dump Locking Error! Couldn't obtain an " \ "exclusive yum lock in %d seconds. Giving up." % options.yum_lock_timeout return 200 else: break return dump_packages(yb, options.package_list, options.output_provides) # Ensure we clear the lock and cleanup any resources finally: try: yb.closeRpmDB() if lock_obtained == True: yb.doUnlock(YUM_PID_FILE) except Errors.LockError, e: print >> sys.stderr, "yum-dump Unlock Error: %s" % e return 200 # Preserve order of enable/disable repo args like yum does def gather_repo_opts(option, opt, value, parser): if getattr(parser.values, option.dest, None) is None: setattr(parser.values, option.dest, []) getattr(parser.values, option.dest).append((opt, value.split(','))) def main(): usage = "Usage: %prog [options]\n" + \ "Output a list of installed, available and re-installable packages via yum" parser = OptionParser(usage=usage) parser.add_option("-C", "--cache", action="store_true", dest="cache", default=False, help="run entirely from cache, don't update cache") parser.add_option("-o", "--options", action="store_true", dest="output_options", default=False, help="output select yum options useful to Chef") parser.add_option("-p", "--installed-provides", action="store_const", const="installed", dest="output_provides", default="none", help="output Provides for installed packages, big/wide output") parser.add_option("-P", "--all-provides", action="store_const", const="all", dest="output_provides", default="none", help="output Provides for all package, slow, big/wide output") parser.add_option("-i", "--installed", action="store_const", const="installed", dest="package_list", default="all", help="output only installed packages") parser.add_option("-a", "--available", action="store_const", const="available", dest="package_list", default="all", help="output only available and re-installable packages") parser.add_option("--enablerepo", action="callback", callback=gather_repo_opts, type="string", dest="repo_control", default=[], help="enable disabled repositories by id or glob") parser.add_option("--disablerepo", action="callback", callback=gather_repo_opts, type="string", dest="repo_control", default=[], help="disable repositories by id or glob") parser.add_option("--yum-lock-timeout", action="store", type="int", dest="yum_lock_timeout", default=30, help="Time in seconds to wait for yum process lock") (options, args) = parser.parse_args() try: return yum_dump(options) except yum.Errors.RepoError, e: print >> sys.stderr, "yum-dump Repository Error: %s" % e return 1 except yum.Errors.YumBaseError, e: print >> sys.stderr, "yum-dump General Error: %s" % e return 1 try: status = main() # Suppress a nasty broken pipe error when output is piped to utilities like 'head' except IOError, e: if e.errno == errno.EPIPE: sys.exit(1) else: raise sys.exit(status) chef-12.3.0/lib/chef/provider/package/easy_install.rb0000644000004100000410000001056412520074675022464 0ustar www-datawww-data# # Author:: Joe Williams () # Copyright:: Copyright (c) 2009 Joe Williams # 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/package' require 'chef/mixin/command' require 'chef/resource/package' class Chef class Provider class Package class EasyInstall < Chef::Provider::Package provides :easy_install_package def install_check(name) check = false begin # first check to see if we can import it output = shell_out!("#{python_binary_path} -c \"import #{name}\"", :returns=>[0,1]).stderr if output.include? "ImportError" # then check to see if its on the path output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout if output.downcase.include? "#{name.downcase}" check = true end else check = true end rescue # it's probably not installed end check end def easy_install_binary_path path = @new_resource.easy_install_binary path ? path : 'easy_install' end def python_binary_path path = @new_resource.python_binary path ? path : 'python' end def module_name m = @new_resource.module_name m ? m : @new_resource.name end def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(nil) # get the currently installed version if installed package_version = nil if install_check(module_name) begin output = shell_out!("#{python_binary_path} -c \"import #{module_name}; print #{module_name}.__version__\"").stdout package_version = output.strip rescue output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout output_array = output.gsub(/[\[\]]/,'').split(/\s*,\s*/) package_path = "" output_array.each do |entry| if entry.downcase.include?(@new_resource.package_name) package_path = entry end end package_path[/\S\S(.*)\/(.*)-(.*)-py(.*).egg\S/] package_version = $3 end end if package_version == @new_resource.version Chef::Log.debug("#{@new_resource} at version #{@new_resource.version}") @current_resource.version(@new_resource.version) else Chef::Log.debug("#{@new_resource} at version #{package_version}") @current_resource.version(package_version) end @current_resource end def candidate_version return @candidate_version if @candidate_version # do a dry run to get the latest version result = shell_out!("#{easy_install_binary_path} -n #{@new_resource.package_name}", :returns=>[0,1]) @candidate_version = result.stdout[/(.*)Best match: (.*) (.*)$/, 3] @candidate_version end def install_package(name, version) run_command(:command => "#{easy_install_binary_path}#{expand_options(@new_resource.options)} \"#{name}==#{version}\"") end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) run_command(:command => "#{easy_install_binary_path }#{expand_options(@new_resource.options)} -m #{name}") end def purge_package(name, version) remove_package(name, version) end end end end end chef-12.3.0/lib/chef/provider/package/solaris.rb0000644000004100000410000001271712520074675021453 0ustar www-datawww-data# # Author:: Toomas Pelberg () # Copyright:: Copyright (c) 2010 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/package' require 'chef/mixin/command' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class Solaris < Chef::Provider::Package include Chef::Mixin::GetSourceFromPackage provides :solaris_package, os: "solaris2" # def initialize(*args) # super # @current_resource = Chef::Resource::Package.new(@new_resource.name) # end def define_resource_requirements super requirements.assert(:install) do |a| a.assertion { @new_resource.source } a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install" end requirements.assert(:all_actions) do |a| a.assertion { !@new_resource.source || @package_source_found } a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}" a.whyrun "would assume #{@new_resource.source} would be have previously been made available" end end def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @new_resource.version(nil) if @new_resource.source @package_source_found = ::File.exists?(@new_resource.source) if @package_source_found Chef::Log.debug("#{@new_resource} checking pkg status") shell_out("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}").stdout.each_line do |line| case line when /VERSION:\s+(.+)/ @new_resource.version($1) end end end end Chef::Log.debug("#{@new_resource} checking install state") status = shell_out("pkginfo -l #{@current_resource.package_name}") status.stdout.each_line do |line| case line when /VERSION:\s+(.+)/ Chef::Log.debug("#{@new_resource} version #{$1} is already installed") @current_resource.version($1) end end unless status.exitstatus == 0 || status.exitstatus == 1 raise Chef::Exceptions::Package, "pkginfo failed - #{status.inspect}!" end unless @current_resource.version.nil? @current_resource.version(nil) end @current_resource end def candidate_version return @candidate_version if @candidate_version status = shell_out("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}") status.stdout.each_line do |line| case line when /VERSION:\s+(.+)/ @candidate_version = $1 @new_resource.version($1) Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}") end end unless status.exitstatus == 0 raise Chef::Exceptions::Package, "pkginfo -l -d #{@new_resource.source} - #{status.inspect}!" end @candidate_version end def install_package(name, version) Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}") if @new_resource.options.nil? if ::File.directory?(@new_resource.source) # CHEF-4469 command = "pkgadd -n -d #{@new_resource.source} #{@new_resource.package_name}" else command = "pkgadd -n -d #{@new_resource.source} all" end shell_out!(command) Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}") else if ::File.directory?(@new_resource.source) # CHEF-4469 command = "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}" else command = "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} all" end shell_out!(command) Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}") end end def remove_package(name, version) if @new_resource.options.nil? shell_out!( "pkgrm -n #{name}" ) Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}") else shell_out!( "pkgrm -n#{expand_options(@new_resource.options)} #{name}" ) Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}") end end end end end end chef-12.3.0/lib/chef/provider/package/macports.rb0000644000004100000410000000666612520074675021635 0ustar www-datawww-dataclass Chef class Provider class Package class Macports < Chef::Provider::Package provides :macports_package provides :package, os: "darwin" def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(current_installed_version) Chef::Log.debug("#{@new_resource} current version is #{@current_resource.version}") if @current_resource.version @candidate_version = macports_candidate_version if !@new_resource.version and !@candidate_version raise Chef::Exceptions::Package, "Could not get a candidate version for this package -- #{@new_resource.name} does not seem to be a valid package!" end Chef::Log.debug("#{@new_resource} candidate version is #{@candidate_version}") if @candidate_version @current_resource end def current_installed_version command = "port installed #{@new_resource.package_name}" output = get_response_from_command(command) response = nil output.each_line do |line| match = line.match(/^.+ @([^\s]+) \(active\)$/) response = match[1] if match end response end def macports_candidate_version command = "port info --version #{@new_resource.package_name}" output = get_response_from_command(command) match = output.match(/^version: (.+)$/) match ? match[1] : nil end def install_package(name, version) unless @current_resource.version == version command = "port#{expand_options(@new_resource.options)} install #{name}" command << " @#{version}" if version and !version.empty? shell_out!(command) end end def purge_package(name, version) command = "port#{expand_options(@new_resource.options)} uninstall #{name}" command << " @#{version}" if version and !version.empty? shell_out!(command) end def remove_package(name, version) command = "port#{expand_options(@new_resource.options)} deactivate #{name}" command << " @#{version}" if version and !version.empty? shell_out!(command) end def upgrade_package(name, version) # Saving this to a variable -- weird rSpec behavior # happens otherwise... current_version = @current_resource.version if current_version.nil? or current_version.empty? # Macports doesn't like when you upgrade a package # that hasn't been installed. install_package(name, version) elsif current_version != version shell_out!( "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}" ) end end private def get_response_from_command(command) output = nil status = shell_out(command) begin output = status.stdout rescue Exception raise Chef::Exceptions::Package, "Could not read from STDOUT on command: #{command}" end unless status.exitstatus == 0 || status.exitstatus == 1 raise Chef::Exceptions::Package, "#{command} failed - #{status.insect}!" end output end end end end end chef-12.3.0/lib/chef/provider/package/rubygems.rb0000644000004100000410000005450112520074675021631 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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 'uri' require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' # Class methods on Gem are defined in rubygems require 'rubygems' # Ruby 1.9's gem_prelude can interact poorly with loading the full rubygems # explicitly like this. Make sure rubygems/specification is always last in this # list require 'rubygems/version' require 'rubygems/dependency' require 'rubygems/spec_fetcher' require 'rubygems/platform' # 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' class Chef class Provider class Package class Rubygems < Chef::Provider::Package class GemEnvironment # HACK: trigger gem config load early. Otherwise it can get lazy # loaded during operations where we've set Gem.sources to an # alternate value and overwrite it with the defaults. Gem.configuration DEFAULT_UNINSTALLER_OPTS = {:ignore => true, :executables => true} ## # The paths where rubygems should search for installed gems. # Implemented by subclasses. def gem_paths raise NotImplementedError end ## # A rubygems source index containing the list of gemspecs for all # available gems in the gem installation. # Implemented by subclasses # === Returns # Gem::SourceIndex def gem_source_index raise NotImplementedError end ## # A rubygems specification object containing the list of gemspecs for all # available gems in the gem installation. # Implemented by subclasses # For rubygems >= 1.8.0 # === Returns # Gem::Specification def gem_specification raise NotImplementedError end ## # Lists the installed versions of +gem_name+, constrained by the # version spec in +gem_dep+ # === Arguments # Gem::Dependency +gem_dep+ is a Gem::Dependency object, its version # specification constrains which gems are returned. # === Returns # [Gem::Specification] an array of Gem::Specification objects def installed_versions(gem_dep) if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0') gem_specification.find_all_by_name(gem_dep.name, gem_dep.requirement) else gem_source_index.search(gem_dep) end end ## # Yields to the provided block with rubygems' source list set to the # list provided. Always resets the list when the block returns or # raises an exception. def with_gem_sources(*sources) sources.compact! original_sources = Gem.sources Gem.sources = sources unless sources.empty? yield ensure Gem.sources = original_sources 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) and Gem::Package.respond_to?(:open) 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 constraints in +gem_dependency+ # === Returns # Gem::Version a singular gem version object is returned if the gem # is available # 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 = 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 else # This is probably going to end badly... logger.warn { "#{@new_resource} gem package #{source} does not satisfy the requirements #{gem_dependency.to_s}" } nil end end ## # Finds the newest version that satisfies the constraints of # +gem_dependency+. The version is determined from the cache or a # round-trip to the server as needed. The architecture and gem # sources will be set before making the query. # === Returns # Gem::Version a singular gem version object is returned if the gem # is available # nil returns nil if the gem could not be found def candidate_version_from_remote(gem_dependency, *sources) raise NotImplementedError end ## # Find the newest gem version available from Gem.sources that satisfies # the constraints of +gem_dependency+ def find_newest_remote_version(gem_dependency, *sources) 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 version = spec && spec.version if version 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.to_a.join(', ')}]" : "[#{sources.join(', ')}]" logger.warn { "#{@new_resource} failed to find gem #{gem_dependency} from #{source_list}" } nil end end ## # Installs a gem via the rubygems ruby API. # === Options # :sources rubygems servers to use # Other options are passed to Gem::DependencyInstaller.new def install(gem_dependency, options={}) with_gem_sources(*options.delete(:sources)) do with_correct_verbosity do dependency_installer(options).install(gem_dependency) end end end ## # Uninstall the gem +gem_name+ via the rubygems ruby API. If # +gem_version+ is provided, only that version will be uninstalled. # Otherwise, all versions are uninstalled. # === Options # Options are passed to Gem::Uninstaller.new def uninstall(gem_name, gem_version=nil, opts={}) gem_version ? opts[:version] = gem_version : opts[:all] = true with_correct_verbosity do uninstaller(gem_name, opts).uninstall end end ## # Set rubygems' user interaction to ConsoleUI or SilentUI depending # on our current debug level def with_correct_verbosity Gem::DefaultUserInteraction.ui = Chef::Log.debug? ? Gem::ConsoleUI.new : Gem::SilentUI.new yield end def dependency_installer(opts={}) Gem::DependencyInstaller.new(opts) end def uninstaller(gem_name, opts={}) Gem::Uninstaller.new(gem_name, DEFAULT_UNINSTALLER_OPTS.merge(opts)) end private def logger Chef::Log.logger end end class CurrentGemEnvironment < GemEnvironment def gem_paths Gem.path end def gem_source_index Gem.source_index end def gem_specification Gem::Specification end def candidate_version_from_remote(gem_dependency, *sources) with_gem_sources(*sources) do find_newest_remote_version(gem_dependency, *sources) end end end class AlternateGemEnvironment < GemEnvironment JRUBY_PLATFORM = /(:?universal|x86_64|x86)\-java\-[0-9\.]+/ def self.gempath_cache @gempath_cache ||= {} end def self.platform_cache @platform_cache ||= {} end include Chef::Mixin::ShellOut attr_reader :gem_binary_location def initialize(gem_binary_location) @gem_binary_location = gem_binary_location end def gem_paths if self.class.gempath_cache.key?(@gem_binary_location) self.class.gempath_cache[@gem_binary_location] else # shellout! is a fork/exec which won't work on windows shell_style_paths = shell_out!("#{@gem_binary_location} env gempath").stdout # on windows, the path separator is (usually? always?) semicolon paths = shell_style_paths.split(::File::PATH_SEPARATOR).map { |path| path.strip } self.class.gempath_cache[@gem_binary_location] = paths end end def gem_source_index @source_index ||= Gem::SourceIndex.from_gems_in(*gem_paths.map { |p| p + '/specifications' }) end def gem_specification # Only once, dirs calls a reset unless @specification Gem::Specification.dirs = gem_paths @specification = Gem::Specification end @specification end ## # Attempt to detect the correct platform settings for the target gem # environment. # # In practice, this only makes a difference if different versions are # available depending on platform, and only if the target gem # environment has a radically different platform (i.e., jruby), so we # just try to detect jruby and fall back to the current platforms # (Gem.platforms) if we don't detect it. # # === Returns # [String|Gem::Platform] returns an array of Gem::Platform-compatible # objects, i.e., Strings that are valid for Gem::Platform or actual # Gem::Platform objects. def gem_platforms if self.class.platform_cache.key?(@gem_binary_location) self.class.platform_cache[@gem_binary_location] else gem_environment = shell_out!("#{@gem_binary_location} env").stdout if jruby = gem_environment[JRUBY_PLATFORM] self.class.platform_cache[@gem_binary_location] = ['ruby', Gem::Platform.new(jruby)] else self.class.platform_cache[@gem_binary_location] = Gem.platforms end end end def with_gem_platforms(*alt_gem_platforms) alt_gem_platforms.flatten! original_gem_platforms = Gem.platforms Gem.platforms = alt_gem_platforms yield ensure Gem.platforms = original_gem_platforms end def candidate_version_from_remote(gem_dependency, *sources) with_gem_sources(*sources) do with_gem_platforms(*gem_platforms) do find_newest_remote_version(gem_dependency, *sources) end end end end attr_reader :gem_env attr_reader :cleanup_gem_env def logger Chef::Log.logger end provides :chef_gem provides :gem_package include Chef::Mixin::GetSourceFromPackage def initialize(new_resource, run_context=nil) super @cleanup_gem_env = true if new_resource.gem_binary if new_resource.options && new_resource.options.kind_of?(Hash) msg = "options cannot be given as a hash when using an explicit gem_binary\n" msg << "in #{new_resource} from #{new_resource.source_line}" raise ArgumentError, msg end @gem_env = AlternateGemEnvironment.new(new_resource.gem_binary) Chef::Log.debug("#{@new_resource} using gem '#{new_resource.gem_binary}'") elsif is_omnibus? && (!@new_resource.instance_of? Chef::Resource::ChefGem) # Opscode Omnibus - The ruby that ships inside omnibus is only used for Chef # Default to installing somewhere more functional if new_resource.options && new_resource.options.kind_of?(Hash) msg = [ "Gem options must be passed to gem_package as a string instead of a hash when", "using this installation of Chef because it runs with its own packaged Ruby. A hash", "may only be used when installing a gem to the same Ruby installation that Chef is", "running under. See https://docs.chef.io/resource_gem_package.html for more information.", "Error raised at #{new_resource} from #{new_resource.source_line}", ].join("\n") raise ArgumentError, msg end gem_location = find_gem_by_path @new_resource.gem_binary gem_location @gem_env = AlternateGemEnvironment.new(gem_location) Chef::Log.debug("#{@new_resource} using gem '#{gem_location}'") else @gem_env = CurrentGemEnvironment.new @cleanup_gem_env = false Chef::Log.debug("#{@new_resource} using gem from running ruby environment") end end def is_omnibus? if RbConfig::CONFIG['bindir'] =~ %r!/opt/(opscode|chef)/embedded/bin! Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}") # Omnibus installs to a static path because of linking on unix, find it. true elsif RbConfig::CONFIG['bindir'].sub(/^[\w]:/, '') == "/opscode/chef/embedded/bin" Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}") # windows, with the drive letter removed true else false end end def find_gem_by_path Chef::Log.debug("#{@new_resource} searching for 'gem' binary in path: #{ENV['PATH']}") separator = ::File::ALT_SEPARATOR ? ::File::ALT_SEPARATOR : ::File::SEPARATOR path_to_first_gem = ENV['PATH'].split(::File::PATH_SEPARATOR).select { |path| ::File.exists?(path + separator + "gem") }.first raise Chef::Exceptions::FileNotFound, "Unable to find 'gem' binary in path: #{ENV['PATH']}" if path_to_first_gem.nil? path_to_first_gem + separator + "gem" end def gem_dependency Gem::Dependency.new(@new_resource.package_name, @new_resource.version) end def source_is_remote? return true if @new_resource.source.nil? scheme = URI.parse(@new_resource.source).scheme # URI.parse gets confused by MS Windows paths with forward slashes. scheme = nil if scheme =~ /^[a-z]$/ %w{http https}.include?(scheme) rescue URI::InvalidURIError Chef::Log.debug("#{@new_resource} failed to parse source '#{@new_resource.source}' as a URI, assuming a local path") false end def current_version #raise 'todo' # If one or more matching versions are installed, the newest of them # is the current version if !matching_installed_versions.empty? gemspec = matching_installed_versions.last logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}"} gemspec # If no version matching the requirements exists, the latest installed # version is the current version. elsif !all_installed_versions.empty? gemspec = all_installed_versions.last logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" } gemspec else logger.debug { "#{@new_resource} no installed version found for #{gem_dependency.to_s}"} nil end end def matching_installed_versions @matching_installed_versions ||= @gem_env.installed_versions(gem_dependency) end def all_installed_versions @all_installed_versions ||= begin @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, '>= 0')) end end def gem_sources @new_resource.source ? Array(@new_resource.source) : nil end def load_current_resource @current_resource = Chef::Resource::Package::GemPackage.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) if current_spec = current_version @current_resource.version(current_spec.version.to_s) end @current_resource end def cleanup_after_converge if @cleanup_gem_env logger.debug { "#{@new_resource} resetting gem environment to default" } Gem.clear_paths end end def candidate_version @candidate_version ||= begin if target_version_already_installed?(@current_resource.version, @new_resource.version) nil elsif source_is_remote? @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s else @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s end end end def target_version_already_installed?(current_version, new_version) return false unless current_version return false if new_version.nil? Gem::Requirement.new(new_version).satisfied_by?(Gem::Version.new(current_version)) end ## # Installs the gem, using either the gems API or shelling out to `gem` # according to the following criteria: # 1. Use gems API (Gem::DependencyInstaller) by default # 2. shell out to `gem install` when a String of options is given # 3. use gems API with options if a hash of options is given def install_package(name, version) if source_is_remote? && @new_resource.gem_binary.nil? if @new_resource.options.nil? @gem_env.install(gem_dependency, :sources => gem_sources) elsif @new_resource.options.kind_of?(Hash) options = @new_resource.options options[:sources] = gem_sources @gem_env.install(gem_dependency, options) else install_via_gem_command(name, version) end elsif @new_resource.gem_binary.nil? @gem_env.install(@new_resource.source) else install_via_gem_command(name,version) end true end def gem_binary_path @new_resource.gem_binary || 'gem' end def install_via_gem_command(name, version) if @new_resource.source =~ /\.gem$/i name = @new_resource.source elsif @new_resource.clear_sources src = ' --clear-sources' src << (@new_resource.source && " --source=#{@new_resource.source}" || '') else src = @new_resource.source && " --source=#{@new_resource.source} --source=https://rubygems.org" end if !version.nil? && version.length > 0 shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env=>nil) else shell_out!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src}#{opts}", :env=>nil) end end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) if @new_resource.gem_binary.nil? if @new_resource.options.nil? @gem_env.uninstall(name, version) elsif @new_resource.options.kind_of?(Hash) @gem_env.uninstall(name, version, @new_resource.options) else uninstall_via_gem_command(name, version) end else uninstall_via_gem_command(name, version) end end def uninstall_via_gem_command(name, version) if version shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env=>nil) else shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env=>nil) end end def purge_package(name, version) remove_package(name, version) end private def opts expand_options(@new_resource.options) end end end end end chef-12.3.0/lib/chef/provider/package/aix.rb0000644000004100000410000001260712520074675020556 0ustar www-datawww-data# # Author:: Deepali Jagtap # 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/package' require 'chef/mixin/command' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class Aix < Chef::Provider::Package provides :bff_package, os: "aix" include Chef::Mixin::GetSourceFromPackage def define_resource_requirements super requirements.assert(:install) do |a| a.assertion { @new_resource.source } a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install" end requirements.assert(:all_actions) do |a| a.assertion { !@new_resource.source || @package_source_found } a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}" a.whyrun "would assume #{@new_resource.source} would be have previously been made available" end end def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @new_resource.version(nil) if @new_resource.source @package_source_found = ::File.exists?(@new_resource.source) if @package_source_found Chef::Log.debug("#{@new_resource} checking pkg status") ret = shell_out("installp -L -d #{@new_resource.source}") ret.stdout.each_line do | line | case line when /#{@new_resource.package_name}:/ fields = line.split(":") @new_resource.version(fields[2]) end end end end Chef::Log.debug("#{@new_resource} checking install state") ret = shell_out("lslpp -lcq #{@current_resource.package_name}") ret.stdout.each_line do | line | case line when /#{@current_resource.package_name}/ fields = line.split(":") Chef::Log.debug("#{@new_resource} version #{fields[2]} is already installed") @current_resource.version(fields[2]) end end unless ret.exitstatus == 0 || ret.exitstatus == 1 raise Chef::Exceptions::Package, "lslpp failed - #{ret.format_for_exception}!" end @current_resource end def candidate_version return @candidate_version if @candidate_version ret = shell_out("installp -L -d #{@new_resource.source}") ret.stdout.each_line do | line | case line when /\w:#{Regexp.escape(@new_resource.package_name)}:(.*)/ fields = line.split(":") @candidate_version = fields[2] @new_resource.version(fields[2]) Chef::Log.debug("#{@new_resource} setting install candidate version to #{@candidate_version}") end end unless ret.exitstatus == 0 raise Chef::Exceptions::Package, "installp -L -d #{@new_resource.source} - #{ret.format_for_exception}!" end @candidate_version end # # The install/update action needs to be tested with various kinds of packages # on AIX viz. packages with or without licensing file dependencies, packages # with dependencies on other packages which will help to test additional # options of installp. # So far, the code has been tested only with standalone packages. # def install_package(name, version) Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}") if @new_resource.options.nil? shell_out!( "installp -aYF -d #{@new_resource.source} #{@new_resource.package_name}" ) Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}") else shell_out!( "installp -aYF #{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}" ) Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}") end end alias_method :upgrade_package, :install_package def remove_package(name, version) if @new_resource.options.nil? shell_out!( "installp -u #{name}" ) Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}") else shell_out!( "installp -u #{expand_options(@new_resource.options)} #{name}" ) Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}") end end end end end end chef-12.3.0/lib/chef/provider/package/dpkg.rb0000644000004100000410000001305512520074675020720 0ustar www-datawww-data# # Author:: Bryan McLellan (btm@loftninjas.org) # Copyright:: Copyright (c) 2009 Bryan McLellan # 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/package' require 'chef/mixin/command' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class Dpkg < Chef::Provider::Package # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version DPKG_INFO = /([a-z\d\-\+\.]+)\t([\w\d.~:-]+)/ DPKG_INSTALLED = /^Status: install ok installed/ DPKG_VERSION = /^Version: (.+)$/ provides :dpkg_package, os: "linux" include Chef::Mixin::GetSourceFromPackage def define_resource_requirements super requirements.assert(:install) do |a| a.assertion{ not @new_resource.source.nil? } a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install" end # TODO this was originally written for any action in which .source is provided # but would it make more sense to only look at source if the action is :install? requirements.assert(:all_actions) do |a| a.assertion { @source_exists } a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}" a.whyrun "Assuming it would have been previously downloaded." end end def load_current_resource @source_exists = true @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @new_resource.version(nil) if @new_resource.source @source_exists = ::File.exists?(@new_resource.source) if @source_exists # Get information from the package if supplied Chef::Log.debug("#{@new_resource} checking dpkg status") shell_out("dpkg-deb -W #{@new_resource.source}").stdout.each_line do |line| if pkginfo = DPKG_INFO.match(line) @current_resource.package_name(pkginfo[1]) @new_resource.version(pkginfo[2]) @candidate_version = pkginfo[2] end end else # Source provided but not valid means we can't safely do further processing return end end # Check to see if it is installed package_installed = nil Chef::Log.debug("#{@new_resource} checking install state") status = shell_out("dpkg -s #{@current_resource.package_name}") status.stdout.each_line do |line| case line when DPKG_INSTALLED package_installed = true when DPKG_VERSION if package_installed Chef::Log.debug("#{@new_resource} current version is #{$1}") @current_resource.version($1) end end end unless status.exitstatus == 0 || status.exitstatus == 1 raise Chef::Exceptions::Package, "dpkg failed - #{status.inspect}!" end @current_resource end def install_package(name, version) Chef::Log.info("#{@new_resource} installing #{@new_resource.source}") run_noninteractive( "dpkg -i#{expand_options(@new_resource.options)} #{@new_resource.source}" ) end def remove_package(name, version) Chef::Log.info("#{@new_resource} removing #{@new_resource.package_name}") run_noninteractive( "dpkg -r#{expand_options(@new_resource.options)} #{@new_resource.package_name}" ) end def purge_package(name, version) Chef::Log.info("#{@new_resource} purging #{@new_resource.package_name}") run_noninteractive( "dpkg -P#{expand_options(@new_resource.options)} #{@new_resource.package_name}" ) end def upgrade_package(name, version) install_package(name, version) end def preseed_package(preseed_file) Chef::Log.info("#{@new_resource} pre-seeding package installation instructions") run_noninteractive("debconf-set-selections #{preseed_file}") end def reconfig_package(name, version) Chef::Log.info("#{@new_resource} reconfiguring") run_noninteractive("dpkg-reconfigure #{name}") end # Runs command via shell_out with magic environment to disable # interactive prompts. Command is run with default localization rather # than forcing locale to "C", so command output may not be stable. # # FIXME: This should be "LC_ALL" => "en_US.UTF-8" in order to stabilize the output and get UTF-8 def run_noninteractive(command) shell_out!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }) end end end end end chef-12.3.0/lib/chef/provider/package/homebrew.rb0000644000004100000410000001164012520074675021601 0ustar www-datawww-data# # Author:: Joshua Timberman () # Author:: Graeme Mathieson () # # Copyright 2011-2013, Opscode, Inc. # Copyright 2014, Chef Software, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'etc' require 'chef/mixin/homebrew_user' class Chef class Provider class Package class Homebrew < Chef::Provider::Package provides :homebrew_package provides :package, os: "darwin" include Chef::Mixin::HomebrewUser def load_current_resource self.current_resource = Chef::Resource::Package.new(new_resource.name) current_resource.package_name(new_resource.package_name) current_resource.version(current_installed_version) Chef::Log.debug("#{new_resource} current version is #{current_resource.version}") if current_resource.version @candidate_version = candidate_version Chef::Log.debug("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version current_resource end def install_package(name, version) unless current_resource.version == version brew('install', new_resource.options, name) end end def upgrade_package(name, version) current_version = current_resource.version if current_version.nil? or current_version.empty? install_package(name, version) elsif current_version != version brew('upgrade', new_resource.options, name) end end def remove_package(name, version) if current_resource.version brew('uninstall', new_resource.options, name) end end # Homebrew doesn't really have a notion of purging, do a "force remove" def purge_package(name, version) new_resource.options((new_resource.options || '') << ' --force').strip remove_package(name, version) end def brew(*args) get_response_from_command("brew #{args.join(' ')}") end # We implement a querying method that returns the JSON-as-Hash # data for a formula per the Homebrew documentation. Previous # implementations of this provider in the homebrew cookbook # performed a bit of magic with the load path to get this # information, but that is not any more robust than using the # command-line interface that returns the same thing. # # https://github.com/Homebrew/homebrew/wiki/Querying-Brew def brew_info @brew_info ||= Chef::JSONCompat.from_json(brew('info', '--json=v1', new_resource.package_name)).first end # Some packages (formula) are "keg only" and aren't linked, # because multiple versions installed can cause conflicts. We # handle this by using the last installed version as the # "current" (as in latest). Otherwise, we will use the version # that brew thinks is linked as the current version. # def current_installed_version if brew_info['keg_only'] if brew_info['installed'].empty? nil else brew_info['installed'].last['version'] end else brew_info['linked_keg'] end end # Packages (formula) available to install should have a # "stable" version, per the Homebrew project's acceptable # formula documentation, so we will rely on that being the # case. Older implementations of this provider in the homebrew # cookbook would fall back to +brew_info['version']+, but the # schema has changed, and homebrew is a constantly rolling # forward project. # # https://github.com/Homebrew/homebrew/wiki/Acceptable-Formulae#stable-versions def candidate_version brew_info['versions']['stable'] end private def get_response_from_command(command) homebrew_uid = find_homebrew_uid(new_resource.respond_to?(:homebrew_user) && new_resource.homebrew_user) homebrew_user = Etc.getpwuid(homebrew_uid) Chef::Log.debug "Executing '#{command}' as user '#{homebrew_user.name}'" output = shell_out!(command, :timeout => 1800, :user => homebrew_uid, :environment => { 'HOME' => homebrew_user.dir, 'RUBYOPT' => nil }) output.stdout.chomp end end end end end chef-12.3.0/lib/chef/provider/package/rpm.rb0000644000004100000410000001073712520074675020575 0ustar www-datawww-data# # Author:: Joshua Timberman () # 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 'chef/provider/package' require 'chef/mixin/command' require 'chef/mixin/shell_out' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' class Chef class Provider class Package class Rpm < Chef::Provider::Package provides :rpm_package, os: [ "linux", "aix" ] include Chef::Mixin::GetSourceFromPackage def define_resource_requirements super requirements.assert(:all_actions) do |a| a.assertion { @package_source_exists } a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}" a.whyrun "Assuming package #{@new_resource.name} would have been made available." end requirements.assert(:all_actions) do |a| a.assertion { !@rpm_status.nil? && (@rpm_status.exitstatus == 0 || @rpm_status.exitstatus == 1) } a.failure_message Chef::Exceptions::Package, "Unable to determine current version due to RPM failure. Detail: #{@rpm_status.inspect}" a.whyrun "Assuming current version would have been determined for package#{@new_resource.name}." end end def load_current_resource @package_source_provided = true @package_source_exists = true @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @new_resource.version(nil) if @new_resource.source unless uri_scheme?(@new_resource.source) || ::File.exists?(@new_resource.source) @package_source_exists = false return end Chef::Log.debug("#{@new_resource} checking rpm status") shell_out!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}").stdout.each_line do |line| case line when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/ @current_resource.package_name($1) @new_resource.version($2) @candidate_version = $2 end end else if Array(@new_resource.action).include?(:install) @package_source_exists = false return end end Chef::Log.debug("#{@new_resource} checking install state") @rpm_status = shell_out("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}") @rpm_status.stdout.each_line do |line| case line when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/ Chef::Log.debug("#{@new_resource} current version is #{$2}") @current_resource.version($2) end end @current_resource end def install_package(name, version) unless @current_resource.version shell_out!( "rpm #{@new_resource.options} -i #{@new_resource.source}" ) else if allow_downgrade shell_out!( "rpm #{@new_resource.options} -U --oldpackage #{@new_resource.source}" ) else shell_out!( "rpm #{@new_resource.options} -U #{@new_resource.source}" ) end end end alias_method :upgrade_package, :install_package def remove_package(name, version) if version shell_out!( "rpm #{@new_resource.options} -e #{name}-#{version}" ) else shell_out!( "rpm #{@new_resource.options} -e #{name}" ) end end private def uri_scheme?(str) scheme = URI.split(str).first return false unless scheme %w(http https ftp file).include?(scheme.downcase) rescue URI::InvalidURIError return false end end end end end chef-12.3.0/lib/chef/provider/package/pacman.rb0000644000004100000410000000666612520074675021244 0ustar www-datawww-data# # Author:: Jan Zimmek () # Copyright:: Copyright (c) 2010 Jan Zimmek # 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/package' require 'chef/mixin/command' require 'chef/resource/package' class Chef class Provider class Package class Pacman < Chef::Provider::Package provides :pacman_package, os: "linux" def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(nil) Chef::Log.debug("#{@new_resource} checking pacman for #{@new_resource.package_name}") status = shell_out("pacman -Qi #{@new_resource.package_name}") status.stdout.each_line do |line| case line when /^Version(\s?)*: (.+)$/ Chef::Log.debug("#{@new_resource} current version is #{$2}") @current_resource.version($2) end end unless status.exitstatus == 0 || status.exitstatus == 1 raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!" end @current_resource end def candidate_version return @candidate_version if @candidate_version repos = ["extra","core","community"] if(::File.exists?("/etc/pacman.conf")) pacman = ::File.read("/etc/pacman.conf") repos = pacman.scan(/\[(.+)\]/).flatten end package_repos = repos.map {|r| Regexp.escape(r) }.join('|') status = shell_out("pacman -Sl") status.stdout.each_line do |line| case line when /^(#{package_repos}) #{Regexp.escape(@new_resource.package_name)} (.+)$/ # $2 contains a string like "4.4.0-1" or "3.10-4 [installed]" # simply split by space and use first token @candidate_version = $2.split(" ").first end end unless status.exitstatus == 0 || status.exitstatus == 1 raise Chef::Exceptions::Package, "pacman failed - #{status.inspect}!" end unless @candidate_version raise Chef::Exceptions::Package, "pacman does not have a version of package #{@new_resource.package_name}" end @candidate_version end def install_package(name, version) shell_out!( "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" ) end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) shell_out!( "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" ) end def purge_package(name, version) remove_package(name, version) end end end end end chef-12.3.0/lib/chef/provider/package/windows.rb0000644000004100000410000000617712520074675021474 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/resource/windows_package' require 'chef/provider/package' require 'chef/util/path_helper' class Chef class Provider class Package class Windows < Chef::Provider::Package provides :package, os: "windows" provides :windows_package, os: "windows" # Depending on the installer, we may need to examine installer_type or # source attributes, or search for text strings in the installer file # binary to determine the installer type for the user. Since the file # must be on disk to do so, we have to make this choice in the provider. require 'chef/provider/package/windows/msi.rb' # load_current_resource is run in Chef::Provider#run_action when not in whyrun_mode? def load_current_resource @new_resource.source(Chef::Util::PathHelper.validate_path(@new_resource.source)) @current_resource = Chef::Resource::WindowsPackage.new(@new_resource.name) @current_resource.version(package_provider.installed_version) @new_resource.version(package_provider.package_version) @current_resource end def package_provider @package_provider ||= begin case installer_type when :msi Chef::Provider::Package::Windows::MSI.new(@new_resource) else raise "Unable to find a Chef::Provider::Package::Windows provider for installer_type '#{installer_type}'" end end end def installer_type @installer_type ||= begin if @new_resource.installer_type @new_resource.installer_type else file_extension = ::File.basename(@new_resource.source).split(".").last.downcase if file_extension == "msi" :msi else raise ArgumentError, "Installer type for Windows Package '#{@new_resource.name}' not specified and cannot be determined from file extension '#{file_extension}'" end end end end # Chef::Provider::Package action_install + action_remove call install_package + remove_package # Pass those calls to the correct sub-provider def install_package(name, version) package_provider.install_package(name, version) end def remove_package(name, version) package_provider.remove_package(name, version) end end end end end chef-12.3.0/lib/chef/provider/package/portage.rb0000644000004100000410000001152512520074675021434 0ustar www-datawww-data# # Author:: Ezra Zygmuntowicz () # 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 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' require 'chef/util/path_helper' class Chef class Provider class Package class Portage < Chef::Provider::Package PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)} def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(nil) category, pkg = %r{^#{PACKAGE_NAME_PATTERN}$}.match(@new_resource.package_name)[1,2] globsafe_category = category ? Chef::Util::PathHelper.escape_glob(category) : nil globsafe_pkg = Chef::Util::PathHelper.escape_glob(pkg) possibilities = Dir["/var/db/pkg/#{globsafe_category || "*"}/#{globsafe_pkg}-*"].map {|d| d.sub(%r{/var/db/pkg/}, "") } versions = possibilities.map do |entry| if(entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)}) [$&, $1] end end.compact if versions.size > 1 atoms = versions.map {|v| v.first }.sort categories = atoms.map {|v| v.split('/')[0] }.uniq if !category && categories.size > 1 raise Chef::Exceptions::Package, "Multiple packages found for #{@new_resource.package_name}: #{atoms.join(" ")}. Specify a category." end elsif versions.size == 1 @current_resource.version(versions.first.last) Chef::Log.debug("#{@new_resource} current version #{$1}") end @current_resource end def parse_emerge(package, txt) availables = {} found_package_name = nil txt.each_line do |line| if line =~ /\*\s+#{PACKAGE_NAME_PATTERN}/ found_package_name = $&.gsub(/\*/, '').strip if package =~ /\// #the category is specified if found_package_name == package availables[found_package_name] = nil end else #the category is not specified if found_package_name.split("/").last == package availables[found_package_name] = nil end end end if line =~ /Latest version available: (.*)/ && availables.has_key?(found_package_name) availables[found_package_name] = $1.strip end end if availables.size > 1 # shouldn't happen if a category is specified so just use `package` raise Chef::Exceptions::Package, "Multiple emerge results found for #{package}: #{availables.keys.join(" ")}. Specify a category." end availables.values.first end def candidate_version return @candidate_version if @candidate_version status = shell_out("emerge --color n --nospinner --search #{@new_resource.package_name.split('/').last}") available, installed = parse_emerge(@new_resource.package_name, status.stdout) @candidate_version = available unless status.exitstatus == 0 raise Chef::Exceptions::Package, "emerge --search failed - #{status.inspect}!" end @candidate_version end def install_package(name, version) pkg = "=#{name}-#{version}" if(version =~ /^\~(.+)/) # If we start with a tilde pkg = "~#{name}-#{$1}" end shell_out!( "emerge -g --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}" ) end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) if(version) pkg = "=#{@new_resource.package_name}-#{version}" else pkg = "#{@new_resource.package_name}" end shell_out!( "emerge --unmerge --color n --nospinner --quiet#{expand_options(@new_resource.options)} #{pkg}" ) end def purge_package(name, version) remove_package(name, version) end end end end end chef-12.3.0/lib/chef/provider/package/paludis.rb0000644000004100000410000000536012520074675021434 0ustar www-datawww-data# # Author:: Vasiliy Tolstov () # Copyright:: Copyright (c) 2014 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/package' require 'chef/resource/package' class Chef class Provider class Package class Paludis < Chef::Provider::Package provides :paludis_package, os: "linux" def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.package_name) @current_resource.package_name(@new_resource.package_name) @current_resource.version(nil) Chef::Log.debug("Checking package status for #{@new_resource.package_name}") installed = false re = Regexp.new('(.*)[[:blank:]](.*)[[:blank:]](.*)$') shell_out!("cave -L warning print-ids -M none -m \"#{@new_resource.package_name}\" -f \"%c/%p %v %r\n\"").stdout.each_line do |line| res = re.match(line) unless res.nil? case res[3] when 'accounts', 'installed-accounts' next when 'installed' installed = true @current_resource.version(res[2]) else @candidate_version = res[2] @current_resource.version(nil) end end end @current_resource end def install_package(name, version) if(version) pkg = "=#{name}-#{version}" else pkg = "#{@new_resource.package_name}" end shell_out!("cave -L warning resolve -x#{expand_options(@new_resource.options)} \"#{pkg}\"",:timeout => @new_resource.timeout) end def upgrade_package(name, version) install_package(name, version) end def remove_package(name, version) if(version) pkg = "=#{@new_resource.package_name}-#{version}" else pkg = "#{@new_resource.package_name}" end shell_out!("cave -L warning uninstall -x#{expand_options(@new_resource.options)} \"#{pkg}\"") end def purge_package(name, version) remove_package(name, version) end end end end end chef-12.3.0/lib/chef/provider/service.rb0000644000004100000410000001214212520074675020034 0ustar www-datawww-data# # Author:: AJ Christensen () # 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 'chef/mixin/command' require 'chef/provider' class Chef class Provider class Service < Chef::Provider include Chef::Mixin::Command def initialize(new_resource, run_context) super @enabled = nil end def whyrun_supported? true end def load_new_resource_state # If the user didn't specify a change in enabled state, # it will be the same as the old resource if ( @new_resource.enabled.nil? ) @new_resource.enabled(@current_resource.enabled) end if ( @new_resource.running.nil? ) @new_resource.running(@current_resource.running) end end def shared_resource_requirements end def define_resource_requirements requirements.assert(:reload) do |a| a.assertion { @new_resource.supports[:reload] || @new_resource.reload_command } a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload" # if a service is not declared to support reload, that won't # typically change during the course of a run - so no whyrun # alternative here. end end def action_enable if @current_resource.enabled Chef::Log.debug("#{@new_resource} already enabled - nothing to do") else converge_by("enable service #{@new_resource}") do enable_service Chef::Log.info("#{@new_resource} enabled") end end load_new_resource_state @new_resource.enabled(true) end def action_disable if @current_resource.enabled converge_by("disable service #{@new_resource}") do disable_service Chef::Log.info("#{@new_resource} disabled") end else Chef::Log.debug("#{@new_resource} already disabled - nothing to do") end load_new_resource_state @new_resource.enabled(false) end def action_start unless @current_resource.running converge_by("start service #{@new_resource}") do start_service Chef::Log.info("#{@new_resource} started") end else Chef::Log.debug("#{@new_resource} already running - nothing to do") end load_new_resource_state @new_resource.running(true) end def action_stop if @current_resource.running converge_by("stop service #{@new_resource}") do stop_service Chef::Log.info("#{@new_resource} stopped") end else Chef::Log.debug("#{@new_resource} already stopped - nothing to do") end load_new_resource_state @new_resource.running(false) end def action_restart converge_by("restart service #{@new_resource}") do restart_service Chef::Log.info("#{@new_resource} restarted") end load_new_resource_state @new_resource.running(true) end def action_reload if @current_resource.running converge_by("reload service #{@new_resource}") do reload_service Chef::Log.info("#{@new_resource} reloaded") end end load_new_resource_state end def enable_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable" end def disable_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable" end def start_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :start" end def stop_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :stop" end def restart_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :restart" end def reload_service raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload" end protected def default_init_command if @new_resource.init_command @new_resource.init_command elsif self.instance_variable_defined?(:@init_command) @init_command end end def custom_command_for_action?(action) method_name = "#{action}_command".to_sym @new_resource.respond_to?(method_name) && !!@new_resource.send(method_name) end end end end chef-12.3.0/lib/chef/provider/template.rb0000644000004100000410000000406012520074675020207 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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/template_finder' require 'chef/provider/file' require 'chef/deprecation/provider/template' require 'chef/deprecation/warnings' class Chef class Provider class Template < Chef::Provider::File provides :template extend Chef::Deprecation::Warnings include Chef::Deprecation::Provider::Template add_deprecation_warnings_for(Chef::Deprecation::Provider::Template.instance_methods) def initialize(new_resource, run_context) @content_class = Chef::Provider::Template::Content super end def load_current_resource @current_resource = Chef::Resource::Template.new(@new_resource.name) super end def define_resource_requirements super requirements.assert(:create, :create_if_missing) do |a| a.assertion { ::File::exists?(content.template_location) } a.failure_message "Template source #{content.template_location} could not be found." a.whyrun "Template source #{content.template_location} does not exist. Assuming it would have been created." a.block_action! end end private def managing_content? return true if @new_resource.checksum return true if !@new_resource.source.nil? && @action != :create_if_missing false end end end end chef-12.3.0/lib/chef/provider/batch.rb0000644000004100000410000000204712520074675017460 0ustar www-datawww-data# # Author:: Adam Edwards () # 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 provides :batch, os: "windows" 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 chef-12.3.0/lib/chef/audit/0000755000004100000410000000000012520074675015323 5ustar www-datawww-datachef-12.3.0/lib/chef/audit/control_group_data.rb0000644000004100000410000000752012520074675021541 0ustar www-datawww-data# # Author:: Tyler Ball () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'securerandom' class Chef class Audit class AuditData attr_reader :node_name, :run_id, :control_groups attr_accessor :start_time, :end_time def initialize(node_name, run_id) @node_name = node_name @run_id = run_id @control_groups = [] end def add_control_group(control_group) control_groups << control_group end def to_hash { :node_name => node_name, :run_id => run_id, :start_time => start_time, :end_time => end_time, :control_groups => control_groups.collect { |c| c.to_hash } } end end class ControlGroupData attr_reader :name, :status, :number_succeeded, :number_failed, :controls, :metadata def initialize(name, metadata={}) @status = "success" @controls = [] @number_succeeded = 0 @number_failed = 0 @name = name @metadata = metadata end def example_success(control_data) @number_succeeded += 1 control = create_control(control_data) control.status = "success" controls << control control end def example_failure(control_data, details) @number_failed += 1 @status = "failure" control = create_control(control_data) control.details = details if details control.status = "failure" controls << control control end def to_hash # We sort it so the examples appear in the output in the same order # they appeared in the recipe controls.sort! {|x,y| x.line_number <=> y.line_number} h = { :name => name, :status => status, :number_succeeded => number_succeeded, :number_failed => number_failed, :controls => controls.collect { |c| c.to_hash } } # If there is a duplicate key, metadata will overwrite it add_display_only_data(h).merge(metadata) end private def create_control(control_data) ControlData.new(control_data) end # The id and control sequence number are ephemeral data - they are not needed # to be persisted and can be regenerated at will. They are only needed # for display purposes. def add_display_only_data(group) group[:id] = SecureRandom.uuid group[:controls].collect!.with_index do |c, i| # i is zero-indexed, and we want the display one-indexed c[:sequence_number] = i+1 c end group end end class ControlData attr_reader :name, :resource_type, :resource_name, :context, :line_number attr_accessor :status, :details def initialize(control_data={}) control_data.each do |k, v| self.instance_variable_set("@#{k}", v) end end def to_hash h = { :name => name, :status => status, :details => details, :resource_type => resource_type, :resource_name => resource_name } h[:context] = context || [] h end end end end chef-12.3.0/lib/chef/audit/audit_reporter.rb0000644000004100000410000001354112520074675020704 0ustar www-datawww-data# # Author:: Tyler Ball () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/event_dispatch/base' require 'chef/audit/control_group_data' require 'time' class Chef class Audit class AuditReporter < EventDispatch::Base attr_reader :rest_client, :audit_data, :ordered_control_groups, :run_status private :rest_client, :audit_data, :ordered_control_groups, :run_status PROTOCOL_VERSION = '0.1.1' def initialize(rest_client) @rest_client = rest_client # Ruby 1.9.3 and above "enumerate their values in the order that the corresponding keys were inserted." @ordered_control_groups = Hash.new end def run_context run_status.run_context end def audit_phase_start(run_status) Chef::Log.debug("Audit Reporter starting") @audit_data = AuditData.new(run_status.node.name, run_status.run_id) @run_status = run_status end def audit_phase_complete Chef::Log.debug("Audit Reporter completed successfully without errors.") ordered_control_groups.each do |name, control_group| audit_data.add_control_group(control_group) end end # If the audit phase failed, its because there was some kind of error in the framework # that runs tests - normal errors are interpreted as EXAMPLE failures and captured. # We still want to send available audit information to the server so we process the # known control groups. def audit_phase_failed(error) # The stacktrace information has already been logged elsewhere Chef::Log.debug("Audit Reporter failed.") ordered_control_groups.each do |name, control_group| audit_data.add_control_group(control_group) end end def run_completed(node) post_auditing_data end def run_failed(error) post_auditing_data(error) end def control_group_started(name) if ordered_control_groups.has_key?(name) raise Chef::Exceptions::AuditControlGroupDuplicate.new(name) end metadata = run_context.audits[name].metadata ordered_control_groups.store(name, ControlGroupData.new(name, metadata)) end def control_example_success(control_group_name, example_data) control_group = ordered_control_groups[control_group_name] control_group.example_success(example_data) end def control_example_failure(control_group_name, example_data, error) control_group = ordered_control_groups[control_group_name] control_group.example_failure(example_data, error.message) end # If @audit_enabled is nil or true, we want to run audits def auditing_enabled? Chef::Config[:audit_mode] != :disabled end private def post_auditing_data(error = nil) unless auditing_enabled? Chef::Log.debug("Audit Reports are disabled. Skipping sending reports.") return end unless run_status Chef::Log.debug("Run failed before audit mode was initialized, not sending audit report to server") return end audit_data.start_time = iso8601ify(run_status.start_time) audit_data.end_time = iso8601ify(run_status.end_time) audit_history_url = "controls" Chef::Log.debug("Sending audit report (run-id: #{audit_data.run_id})") run_data = audit_data.to_hash if error run_data[:error] = "#{error.class.to_s}: #{error.message}\n#{error.backtrace.join("\n")}" end Chef::Log.debug "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}" # Since we're posting compressed data we can not directly call post_rest which expects JSON begin audit_url = rest_client.create_url(audit_history_url) rest_client.post(audit_url, run_data, headers) rescue StandardError => e if e.respond_to? :response # 404 error code is OK. This means the version of server we're running against doesn't support # audit reporting. Don't alarm failure in this case. if e.response.code == "404" Chef::Log.debug("Server doesn't support audit reporting. Skipping report.") return else # Save the audit report to local disk error_file = "failed-audit-data.json" Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640) Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") end else Chef::Log.error("Failed to post audit report to server (#{e})") end if Chef::Config[:enable_reporting_url_fatals] Chef::Log.error("Reporting fatals enabled. Aborting run.") raise end end end def headers(additional_headers = {}) options = {'X-Ops-Audit-Report-Protocol-Version' => PROTOCOL_VERSION} options.merge(additional_headers) end def encode_gzip(data) "".tap do |out| Zlib::GzipWriter.wrap(StringIO.new(out)){|gz| gz << data } end end def iso8601ify(time) time.utc.iso8601.to_s end end end end chef-12.3.0/lib/chef/audit/runner.rb0000644000004100000410000001525012520074675017164 0ustar www-datawww-data# # Author:: Claire McQuin () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Audit class Runner attr_reader :run_context private :run_context def initialize(run_context) @run_context = run_context end def run setup register_control_groups do_run end def failed? RSpec.world.reporter.failed_examples.size > 0 end def num_failed RSpec.world.reporter.failed_examples.size end def num_total RSpec.world.reporter.examples.size end def exclusion_pattern Regexp.new(".+[\\\/]lib[\\\/]chef[\\\/]") end private # Prepare to run audits: # - Require files # - Configure RSpec # - Configure Specinfra/Serverspec def setup require_deps configure_rspec configure_specinfra end # RSpec uses a global configuration object, RSpec.configuration. We found # there was interference between the configuration for audit-mode and # the configuration for our own spec tests in these cases: # 1. Specinfra and Serverspec modify RSpec.configuration when loading. # 2. Setting output/error streams. # 3. Adding formatters. # 4. Defining example group aliases. # # Moreover, Serverspec loads its DSL methods into the global namespace, # which causes conflicts with the Chef namespace for resources and packages. # # We wait until we're in the audit-phase of the chef-client run to load # these files. This helps with the namespacing problems we saw, and # prevents Specinfra and Serverspec from modifying the RSpec configuration # used by our spec tests. def require_deps require 'rspec' require 'rspec/its' require 'specinfra' require 'specinfra/helper' require 'specinfra/helper/set' require 'serverspec/helper' require 'serverspec/matcher' require 'serverspec/subject' require 'chef/audit/audit_event_proxy' require 'chef/audit/rspec_formatter' Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set) end # Configure RSpec just the way we like it: # - Set location of error and output streams # - Add custom audit-mode formatters # - Explicitly disable :should syntax # - Set :color option according to chef config # - Disable exposure of global DSL def configure_rspec set_streams add_formatters disable_should_syntax RSpec.configure do |c| c.color = Chef::Config[:color] c.expose_dsl_globally = false c.backtrace_exclusion_patterns << exclusion_pattern end end # Set the error and output streams which audit-mode will use to report # human-readable audit information. # # This should always be called before #add_formatters. RSpec won't allow # the output stream to be changed for a formatter once the formatter has # been added. def set_streams RSpec.configuration.output_stream = Chef::Config[:log_location] RSpec.configuration.error_stream = Chef::Config[:log_location] end # Add formatters which we use to # 1. Output human-readable data to the output stream, # 2. Collect JSON data to send back to the analytics server. def add_formatters RSpec.configuration.add_formatter(Chef::Audit::AuditEventProxy) RSpec.configuration.add_formatter(Chef::Audit::RspecFormatter) Chef::Audit::AuditEventProxy.events = run_context.events end # Audit-mode uses RSpec 3. :should syntax is deprecated by default in # RSpec 3, so we explicitly disable it here. # # This can be removed once :should is removed from RSpec. def disable_should_syntax RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = :expect end end end # Set up the backend for Specinfra/Serverspec. :exec is the local system; on Windows, it is :cmd def configure_specinfra if Chef::Platform.windows? Specinfra.configuration.backend = :cmd Specinfra.configuration.os = { :family => 'windows' } else Specinfra.configuration.backend = :exec end end # Iterates through the control groups registered to this run_context, builds an # example group (RSpec::Core::ExampleGroup) object per control group, and # registers the group with the RSpec.world. # # We could just store an array of example groups and not use RSpec.world, # but it may be useful later if we decide to apply our own ordering scheme # or use example group filters. def register_control_groups add_example_group_methods run_context.audits.each do |name, group| ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block) RSpec.world.register(ctl_grp) end end # Add example group method aliases to RSpec. # # __control_group__: Used internally to create example groups from the control # groups saved in the run_context. # control: Used within the context of a control group block, like RSpec's # describe or context. def add_example_group_methods RSpec::Core::ExampleGroup.define_example_group_method :__control_group__ RSpec::Core::ExampleGroup.define_example_group_method :control end # Run the audits! def do_run # RSpec::Core::Runner wants to be initialized with an # RSpec::Core::ConfigurationOptions object, which is used to process # command line configuration arguments. We directly fiddle with the # internal RSpec configuration object, so we give nil here and let # RSpec pick up its own configuration and world. runner = RSpec::Core::Runner.new(nil) runner.run_specs(RSpec.world.ordered_example_groups) end end end end chef-12.3.0/lib/chef/audit/rspec_formatter.rb0000644000004100000410000000227712520074675021057 0ustar www-datawww-data# # Author:: Serdar Sutay () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'rspec/core' class Chef class Audit class RspecFormatter < RSpec::Core::Formatters::DocumentationFormatter RSpec::Core::Formatters.register self, :close # @api public # # Invoked at the very end, `close` allows the formatter to clean # up resources, e.g. open streams, etc. # # @param _notification [NullNotification] (Ignored) def close(_notification) # Normally Rspec closes the streams it's given. We don't want it for Chef. end end end end chef-12.3.0/lib/chef/audit/audit_event_proxy.rb0000644000004100000410000000642312520074675021425 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # RSpec::Support.require_rspec_core "formatters/base_text_formatter" class Chef class Audit class AuditEventProxy < ::RSpec::Core::Formatters::BaseFormatter ::RSpec::Core::Formatters.register self, :stop, :example_group_started # TODO I don't like this, but I don't see another way to pass this in # see rspec files configuration.rb#L671 and formatters.rb#L129 def self.events=(events) @@events = events end def events @@events end def example_group_started(notification) if notification.group.parent_groups.size == 1 # top level `control_group` block desc = notification.group.description Chef::Log.debug("Entered `control_group` block named #{desc}") events.control_group_started(desc) end end def stop(notification) Chef::Log.info("Successfully executed all `control_group` blocks and contained examples") notification.examples.each do |example| control_group_name, control_data = build_control_from(example) e = example.exception if e events.control_example_failure(control_group_name, control_data, e) else events.control_example_success(control_group_name, control_data) end end end private def build_control_from(example) described_class = example.metadata[:described_class] if described_class resource_type = described_class.class.name.split(':')[-1] resource_name = described_class.name end # The following code builds up the context - the list of wrapping `describe` or `control` blocks describe_groups = [] group = example.metadata[:example_group] # If the innermost block has a resource instead of a string, don't include it in context describe_groups.unshift(group[:description]) if described_class.nil? group = group[:parent_example_group] while !group.nil? describe_groups.unshift(group[:description]) group = group[:parent_example_group] end # We know all of our examples each live in a top-level `control_group` block - get this name now outermost_group_desc = describe_groups.shift return outermost_group_desc, { :name => example.description, :desc => example.full_description, :resource_type => resource_type, :resource_name => resource_name, :context => describe_groups, :line_number => example.metadata[:line_number] } end end end end chef-12.3.0/lib/chef/file_access_control/0000755000004100000410000000000012520074675020215 5ustar www-datawww-datachef-12.3.0/lib/chef/file_access_control/windows.rb0000644000004100000410000002436412520074675022245 0ustar www-datawww-data# # Author:: John Keiser () # Author:: Seth Chisamore () # Copyright:: Copyright 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 'chef/win32/security' require 'chef/win32/file' class Chef class FileAccessControl module Windows include Chef::ReservedNames::Win32::API::Security Security = Chef::ReservedNames::Win32::Security ACL = Security::ACL ACE = Security::ACE SID = Security::SID module ClassMethods # We want to mix these in as class methods def writable?(path) ::File.exists?(path) && Chef::ReservedNames::Win32::File.file_access_check( path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE) end end def self.included(base) # When this file is mixed in, make sure we also add the class methods base.send :extend, ClassMethods end def set_all! set_owner! set_group! set_dacl end def set_all set_owner set_group set_dacl end def define_resource_requirements # windows FAC has no assertions end def requires_changes? should_update_dacl? || should_update_owner? || should_update_group? end def describe_changes # FIXME: describe what these are changing from and to changes = [] changes << "change dacl" if should_update_dacl? changes << "change owner" if should_update_owner? changes << "change group" if should_update_group? changes end private # Compare the actual ACL on a resource with the ACL we want. This # ignores explicit ACLs on the target, and does mask prediction (if you # set GENERIC_WRITE, Windows will flip on a whole bunch of other rights # on the file when you save the ACL) def acls_equal(target_acl, actual_acl) if actual_acl.nil? return target_acl.nil? end actual_acl = actual_acl.select { |ace| !ace.inherited? } # When ACLs apply to children, Windows splits them on the file system into two ACLs: # one specific applying to this container, and one generic applying to children. new_target_acl = [] target_acl.each do |target_ace| if target_ace.flags & INHERIT_ONLY_ACE == 0 self_ace = target_ace.dup self_ace.flags = 0 self_ace.mask = securable_object.predict_rights_mask(target_ace.mask) new_target_acl << self_ace end if target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0 children_ace = target_ace.dup children_ace.flags |= INHERIT_ONLY_ACE new_target_acl << children_ace end end return actual_acl == new_target_acl end def existing_descriptor securable_object.security_descriptor end def get_sid(value) if value.kind_of?(String) SID.from_account(value) elsif value.kind_of?(SID) value else raise "Must specify username, group or SID: #{value}" end end def securable_object @securable_object ||= begin if file.kind_of?(String) so = Chef::ReservedNames::Win32::Security::SecurableObject.new(file.dup) end raise ArgumentError, "'file' must be a valid path or object of type 'Chef::ReservedNames::Win32::Security::SecurableObject'" unless so.kind_of? Chef::ReservedNames::Win32::Security::SecurableObject so end end def should_update_dacl? return true unless ::File.exists?(file) dacl = target_dacl existing_dacl = existing_descriptor.dacl inherits = target_inherits ( ! inherits.nil? && inherits != existing_descriptor.dacl_inherits? ) || ( dacl && !acls_equal(dacl, existing_dacl) ) end def set_dacl! set_dacl end def set_dacl dacl = target_dacl existing_dacl = existing_descriptor.dacl inherits = target_inherits if ! inherits.nil? && inherits != existing_descriptor.dacl_inherits? # We have to set DACL along with inherits. If rights were not # specified, we need to change only inherited ACLs and leave # explicit ACLs alone. if dacl.nil? && !existing_dacl.nil? dacl = ACL.create(existing_dacl.select { |ace| !ace.inherited? }) end securable_object.set_dacl(dacl, inherits) Chef::Log.info("#{log_string} permissions changed to #{dacl} with inherits of #{inherits}") modified elsif dacl && !acls_equal(dacl, existing_dacl) securable_object.dacl = dacl Chef::Log.info("#{log_string} permissions changed to #{dacl}") modified end end def should_update_group? return true unless ::File.exists?(file) (group = target_group) && (group != existing_descriptor.group) end def set_group! if (group = target_group) Chef::Log.info("#{log_string} group changed to #{group}") securable_object.group = group modified end end def set_group if (group = target_group) && (group != existing_descriptor.group) set_group! end end def should_update_owner? return true unless ::File.exists?(file) (owner = target_owner) && (owner != existing_descriptor.owner) end def set_owner! if owner = target_owner Chef::Log.info("#{log_string} owner changed to #{owner}") securable_object.owner = owner modified end end def set_owner if (owner = target_owner) && (owner != existing_descriptor.owner) set_owner! end end def mode_ace(sid, mode) mask = 0 mask |= GENERIC_READ if mode & 4 != 0 mask |= (GENERIC_WRITE | DELETE) if mode & 2 != 0 mask |= GENERIC_EXECUTE if mode & 1 != 0 return [] if mask == 0 [ ACE.access_allowed(sid, mask) ] end def calculate_mask(permissions) mask = 0 [ permissions ].flatten.each do |permission| case permission when :full_control mask |= GENERIC_ALL when :modify mask |= GENERIC_WRITE | GENERIC_READ | GENERIC_EXECUTE | DELETE when :read mask |= GENERIC_READ when :read_execute mask |= GENERIC_READ | GENERIC_EXECUTE when :write mask |= GENERIC_WRITE else # Otherwise, assume it's an integer specifying the actual flags mask |= permission end end mask end def calculate_flags(rights) # Handle inheritance flags flags = 0 # # Configure child inheritence only if the resource is some # type of a directory. # if resource.is_a? Chef::Resource::Directory case rights[:applies_to_children] when :containers_only flags |= CONTAINER_INHERIT_ACE when :objects_only flags |= OBJECT_INHERIT_ACE when true flags |= CONTAINER_INHERIT_ACE flags |= OBJECT_INHERIT_ACE when nil flags |= CONTAINER_INHERIT_ACE flags |= OBJECT_INHERIT_ACE end end if rights[:applies_to_self] == false flags |= INHERIT_ONLY_ACE end if rights[:one_level_deep] flags |= NO_PROPAGATE_INHERIT_ACE end flags end def target_dacl return nil if resource.rights.nil? && resource.deny_rights.nil? && resource.mode.nil? acls = nil if !resource.deny_rights.nil? acls = [] if acls.nil? resource.deny_rights.each do |rights| mask = calculate_mask(rights[:permissions]) [ rights[:principals] ].flatten.each do |principal| sid = get_sid(principal) flags = calculate_flags(rights) acls.push ACE.access_denied(sid, mask, flags) end end end if !resource.rights.nil? acls = [] if acls.nil? resource.rights.each do |rights| mask = calculate_mask(rights[:permissions]) [ rights[:principals] ].flatten.each do |principal| sid = get_sid(principal) flags = calculate_flags(rights) acls.push ACE.access_allowed(sid, mask, flags) end end end if !resource.mode.nil? acls = [] if acls.nil? mode = (resource.mode.respond_to?(:oct) ? resource.mode.oct : resource.mode.to_i) & 0777 owner = target_owner if owner acls += mode_ace(owner, (mode & 0700) >> 6) elsif mode & 0700 != 0 Chef::Log.warn("Mode #{sprintf("%03o", mode)} includes bits for the owner, but owner is not specified") end group = target_group if group acls += mode_ace(group, (mode & 070) >> 3) elsif mode & 070 != 0 Chef::Log.warn("Mode #{sprintf("%03o", mode)} includes bits for the group, but group is not specified") end acls += mode_ace(SID.Everyone, (mode & 07)) end acls.nil? ? nil : Chef::ReservedNames::Win32::Security::ACL.create(acls) end def target_group return nil if resource.group.nil? sid = get_sid(resource.group) end def target_inherits resource.inherits end def target_owner return nil if resource.owner.nil? sid = get_sid(resource.owner) end end end end chef-12.3.0/lib/chef/file_access_control/unix.rb0000644000004100000410000002316212520074675021531 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Author:: Seth Chisamore () # Copyright:: Copyright (c) 2008-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 'chef/log' class Chef class FileAccessControl module Unix UINT = (1 << 32) UID_MAX = (1 << 32) - 10 module ClassMethods # We want to mix these in as class methods def writable?(path) ::File.writable?(path) end end def self.included(base) # When this file is mixed in, make sure we also add the class methods base.send :extend, ClassMethods end def set_all! set_owner! set_group! set_mode! end def set_all set_owner set_group set_mode end # TODO factor this up def requires_changes? should_update_mode? || should_update_owner? || should_update_group? end def define_resource_requirements uid_from_resource(resource) gid_from_resource(resource) end def describe_changes changes = [] changes << "change mode from '#{mode_to_s(current_mode)}' to '#{mode_to_s(target_mode)}'" if should_update_mode? changes << "change owner from '#{current_resource.owner}' to '#{resource.owner}'" if should_update_owner? changes << "change group from '#{current_resource.group}' to '#{resource.group}'" if should_update_group? changes end def target_uid uid_from_resource(resource) end def current_uid uid_from_resource(current_resource) end def should_update_owner? if target_uid.nil? # the user has not specified a permission on the new resource, so we never manage it with FAC Chef::Log.debug("found target_uid == nil, so no owner was specified on resource, not managing owner") return false elsif current_uid.nil? # the user has specified a permission, and we are creating a file, so always enforce permissions Chef::Log.debug("found current_uid == nil, so we are creating a new file, updating owner") return true elsif target_uid != current_uid # the user has specified a permission, and it does not match the file, so fix the permission Chef::Log.debug("found target_uid != current_uid, updating owner") return true else Chef::Log.debug("found target_uid == current_uid, not updating owner") # the user has specified a permission, but it matches the file, so behave idempotently return false end end def set_owner! unless target_uid.nil? chown(target_uid, nil, file) Chef::Log.info("#{log_string} owner changed to #{target_uid}") modified end end def set_owner set_owner! if should_update_owner? end def target_gid gid_from_resource(resource) end def current_gid gid_from_resource(current_resource) end def gid_from_resource(resource) return nil if resource == nil or resource.group.nil? if resource.group.kind_of?(String) diminished_radix_complement( Etc.getgrnam(resource.group).gid ) elsif resource.group.kind_of?(Integer) resource.group else Chef::Log.error("The `group` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})") raise ArgumentError, "cannot resolve #{resource.group.inspect} to gid, group must be a string or integer" end rescue ArgumentError provider.requirements.assert(:create, :create_if_missing, :touch) do |a| a.assertion { false } a.failure_message(Chef::Exceptions::GroupIDNotFound, "cannot determine group id for '#{resource.group}', does the group exist on this system?") a.whyrun("Assuming group #{resource.group} would have been created") end return nil end def should_update_group? if target_gid.nil? # the user has not specified a permission on the new resource, so we never manage it with FAC Chef::Log.debug("found target_gid == nil, so no group was specified on resource, not managing group") return false elsif current_gid.nil? # the user has specified a permission, and we are creating a file, so always enforce permissions Chef::Log.debug("found current_gid == nil, so we are creating a new file, updating group") return true elsif target_gid != current_gid # the user has specified a permission, and it does not match the file, so fix the permission Chef::Log.debug("found target_gid != current_gid, updating group") return true else Chef::Log.debug("found target_gid == current_gid, not updating group") # the user has specified a permission, but it matches the file, so behave idempotently return false end end def set_group! unless target_gid.nil? chown(nil, target_gid, file) Chef::Log.info("#{log_string} group changed to #{target_gid}") modified end end def set_group set_group! if should_update_group? end def mode_from_resource(res) return nil if res == nil or res.mode.nil? (res.mode.respond_to?(:oct) ? res.mode.oct : res.mode.to_i) & 007777 end def target_mode mode_from_resource(resource) end def mode_to_s(mode) mode.nil? ? "" : "0#{mode.to_s(8)}" end def current_mode mode_from_resource(current_resource) end def should_update_mode? if target_mode.nil? # the user has not specified a permission on the new resource, so we never manage it with FAC Chef::Log.debug("found target_mode == nil, so no mode was specified on resource, not managing mode") return false elsif current_mode.nil? # the user has specified a permission, and we are creating a file, so always enforce permissions Chef::Log.debug("found current_mode == nil, so we are creating a new file, updating mode") return true elsif target_mode != current_mode # the user has specified a permission, and it does not match the file, so fix the permission Chef::Log.debug("found target_mode != current_mode, updating mode") return true else Chef::Log.debug("found target_mode == current_mode, not updating mode") # the user has specified a permission, but it matches the file, so behave idempotently return false end end def set_mode! unless target_mode.nil? chmod(target_mode, file) Chef::Log.info("#{log_string} mode changed to #{target_mode.to_s(8)}") modified end end def set_mode set_mode! if should_update_mode? end def stat if manage_symlink_attrs? @stat ||= File.lstat(file) else @stat ||= File.stat(file) end end def manage_symlink_attrs? @provider.manage_symlink_access? end private def chmod(mode, file) if manage_symlink_attrs? begin File.lchmod(mode, file) rescue NotImplementedError Chef::Log.warn("#{file} mode not changed: File.lchmod is unimplemented on this OS and Ruby version") end else File.chmod(mode, file) end end def chown(uid, gid, file) if manage_symlink_attrs? File.lchown(uid, gid, file) else File.chown(uid, gid, file) end end # Workaround the fact that Ruby's Etc module doesn't believe in negative # uids, so negative uids show up as the diminished radix complement of # a uint. For example, a uid of -2 is reported as 4294967294 def diminished_radix_complement(int) if int > UID_MAX int - UINT else int end end def uid_from_resource(resource) return nil if resource == nil or resource.owner.nil? if resource.owner.kind_of?(String) diminished_radix_complement( Etc.getpwnam(resource.owner).uid ) elsif resource.owner.kind_of?(Integer) resource.owner else Chef::Log.error("The `owner` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})") raise ArgumentError, "cannot resolve #{resource.owner.inspect} to uid, owner must be a string or integer" end rescue ArgumentError provider.requirements.assert(:create, :create_if_missing, :touch) do |a| a.assertion { false } a.failure_message(Chef::Exceptions::UserIDNotFound, "cannot determine user id for '#{resource.owner}', does the user exist on this system?") a.whyrun("Assuming user #{resource.owner} would have been created") end return nil end end end end chef-12.3.0/lib/chef/version.rb0000644000004100000410000000213512520074675016230 0ustar www-datawww-data # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010-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. class Chef CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__))) VERSION = '12.3.0' end # # NOTE: the Chef::Version class is defined in version_class.rb # # NOTE: DO NOT Use the Chef::Version class on Chef::VERSIONs. The # Chef::Version class is for _cookbooks_ only, and cannot handle # pre-release chef-client versions like "10.14.0.rc.2". Please # use Rubygem's Gem::Version class instead. # chef-12.3.0/lib/chef/cookbook_uploader.rb0000644000004100000410000001254512520074675020252 0ustar www-datawww-data require 'set' require 'chef/exceptions' require 'chef/knife/cookbook_metadata' require 'chef/digester' require 'chef/cookbook_manifest' require 'chef/cookbook_version' require 'chef/cookbook/syntax_check' require 'chef/cookbook/file_system_file_vendor' require 'chef/util/threaded_job_queue' require 'chef/sandbox' class Chef class CookbookUploader attr_reader :cookbooks attr_reader :path attr_reader :opts attr_reader :rest attr_reader :concurrency # Creates a new CookbookUploader. # ===Arguments: # * cookbooks::: A Chef::CookbookVersion or array of them describing the # cookbook(s) to be uploaded # * path::: A String or Array of Strings representing the base paths to the # cookbook repositories. # * opts::: (optional) An options Hash # ===Options: # * :force indicates that the uploader should set the force option when # uploading the cookbook. This allows frozen CookbookVersion # documents on the server to be overwritten (otherwise a 409 is # returned by the server) # * :rest A Chef::REST object that you have configured the way you like it. # If you don't provide this, one will be created using the values # in Chef::Config. # * :concurrency An integer that decided how many threads will be used to # perform concurrent uploads def initialize(cookbooks, opts={}) @opts = opts @cookbooks = Array(cookbooks) @rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url]) @concurrency = opts[:concurrency] || 10 @policy_mode = opts[:policy_mode] || false end def upload_cookbooks # Syntax Check validate_cookbooks # generate checksums of cookbook files and create a sandbox checksum_files = {} cookbooks.each do |cb| Chef::Log.info("Saving #{cb.name}") checksum_files.merge!(cb.checksums) end checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo} new_sandbox = rest.post("sandboxes", { :checksums => checksums }) Chef::Log.info("Uploading files") queue = Chef::Util::ThreadedJobQueue.new checksums_to_upload = Set.new # upload the new checksums and commit the sandbox new_sandbox['checksums'].each do |checksum, info| if info['needs_upload'] == true checksums_to_upload << checksum Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}") queue << uploader_function_for(checksum_files[checksum], checksum, info['url'], checksums_to_upload) else Chef::Log.debug("#{checksum_files[checksum]} has not changed") end end queue.process(@concurrency) sandbox_url = new_sandbox['uri'] Chef::Log.debug("Committing sandbox") # Retry if S3 is claims a checksum doesn't exist (the eventual # in eventual consistency) retries = 0 begin rest.put(sandbox_url, {:is_completed => true}) rescue Net::HTTPServerException => e if e.message =~ /^400/ && (retries += 1) <= 5 sleep 2 retry else raise end end # files are uploaded, so save the manifest cookbooks.each do |cb| manifest = Chef::CookbookManifest.new(cb, policy_mode: policy_mode?) save_url = opts[:force] ? manifest.force_save_url : manifest.save_url begin rest.put(save_url, manifest) rescue Net::HTTPServerException => e case e.response.code when "409" raise Chef::Exceptions::CookbookFrozen, "Version #{cb.version} of cookbook #{cb.name} is frozen. Use --force to override." else raise end end end Chef::Log.info("Upload complete!") end def uploader_function_for(file, checksum, url, checksums_to_upload) lambda do # Checksum is the hexadecimal representation of the md5, # but we need the base64 encoding for the content-md5 # header checksum64 = Base64.encode64([checksum].pack("H*")).strip file_contents = File.open(file, "rb") {|f| f.read} # Custom headers. 'content-type' disables JSON serialization of the request body. headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, "accept" => 'application/json' } begin rest.put(url, file_contents, headers) checksums_to_upload.delete(checksum) rescue Net::HTTPServerException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e error_message = "Failed to upload #{file} (#{checksum}) to #{url} : #{e.message}" error_message << "\n#{e.response.body}" if e.respond_to?(:response) Chef::Knife.ui.error(error_message) raise end end end def validate_cookbooks cookbooks.each do |cb| syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cb.name) Chef::Log.info("Validating ruby files") exit(1) unless syntax_checker.validate_ruby_files Chef::Log.info("Validating templates") exit(1) unless syntax_checker.validate_templates Chef::Log.info("Syntax OK") true end end def policy_mode? @policy_mode end end end chef-12.3.0/lib/chef/log.rb0000644000004100000410000000255512520074675015332 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: AJ Christensen (<@aj@opscode.com>) # Author:: Christopher Brown () # 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 'logger' require 'chef/monologger' require 'chef/exceptions' require 'mixlib/log' class Chef class Log extend Mixlib::Log # Force initialization of the primary log device (@logger) init(MonoLogger.new(STDOUT)) class Formatter def self.show_time=(*args) Mixlib::Log::Formatter.show_time = *args end end def self.deprecation(msg=nil, &block) if Chef::Config[:treat_deprecation_warnings_as_errors] error(msg, &block) raise Chef::Exceptions::DeprecatedFeatureError.new(msg) else warn(msg, &block) end end end end chef-12.3.0/lib/chef/formatters/0000755000004100000410000000000012520074675016403 5ustar www-datawww-datachef-12.3.0/lib/chef/formatters/doc.rb0000644000004100000410000002412012520074675017474 0ustar www-datawww-datarequire 'chef/formatters/base' require 'chef/config' class Chef module Formatters #-- # TODO: not sold on the name, but the output is similar to what rspec calls # "specdoc" class Doc < Formatters::Base attr_reader :start_time, :end_time, :successful_audits, :failed_audits private :successful_audits, :failed_audits cli_name(:doc) def initialize(out, err) super @updated_resources = 0 @up_to_date_resources = 0 @successful_audits = 0 @failed_audits = 0 @start_time = Time.now @end_time = @start_time end def elapsed_time end_time - start_time end def run_start(version) puts_line "Starting Chef Client, version #{version}" end def total_resources @up_to_date_resources + @updated_resources end def total_audits successful_audits + failed_audits end def run_completed(node) @end_time = Time.now if Chef::Config[:why_run] puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated" else puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds" if total_audits > 0 puts_line " #{successful_audits}/#{total_audits} controls succeeded" end end end def run_failed(exception) @end_time = Time.now if Chef::Config[:why_run] puts_line "Chef Client failed. #{@updated_resources} resources would have been updated" else puts_line "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds" if total_audits > 0 puts_line " #{successful_audits} controls succeeded" end end end # Called right after ohai runs. def ohai_completed(node) end # Already have a client key, assuming this node has registered. def skipping_registration(node_name, config) end # About to attempt to register as +node_name+ def registration_start(node_name, config) puts_line "Creating a new client identity for #{node_name} using the validator key." end def registration_completed end def node_load_start(node_name, config) end # Failed to load node data from the server def node_load_failed(node_name, exception, config) super end # Default and override attrs from roles have been computed, but not yet applied. # Normal attrs from JSON have been added to the node. def node_load_completed(node, expanded_run_list, config) end # Called before the cookbook collection is fetched from the server. def cookbook_resolution_start(expanded_run_list) puts_line "resolving cookbooks for run list: #{expanded_run_list.inspect}" end # Called when there is an error getting the cookbook collection from the # server. def cookbook_resolution_failed(expanded_run_list, exception) super end # Called when the cookbook collection is returned from the server. def cookbook_resolution_complete(cookbook_collection) end # Called before unneeded cookbooks are removed def cookbook_clean_start end # Called after the file at +path+ is removed. It may be removed if the # cookbook containing it was removed from the run list, or if the file was # removed from the cookbook. def removed_cookbook_file(path) end # Called when cookbook cleaning is finished. def cookbook_clean_complete end # Called before cookbook sync starts def cookbook_sync_start(cookbook_count) puts_line "Synchronizing Cookbooks:" indent end # Called when cookbook +cookbook_name+ has been sync'd def synchronized_cookbook(cookbook_name) puts_line "- #{cookbook_name}" end # Called when an individual file in a cookbook has been updated def updated_cookbook_file(cookbook_name, path) end # Called after all cookbooks have been sync'd. def cookbook_sync_complete unindent end # Called when cookbook loading starts. def library_load_start(file_count) puts_line "Compiling Cookbooks..." end # Called after a file in a cookbook is loaded. def file_loaded(path) end # Called when recipes have been loaded. def recipe_load_complete end # Called before convergence starts def converge_start(run_context) puts_line "Converging #{run_context.resource_collection.all_resources.size} resources" end # Called when the converge phase is finished. def converge_complete unindent if @current_recipe end def converge_failed(e) # Currently a failed converge is handled the same way as a successful converge converge_complete end # Called before audit phase starts def audit_phase_start(run_status) puts_line "Starting audit phase" end def audit_phase_complete puts_line "Auditing complete" end def audit_phase_failed(error) puts_line "" puts_line "Audit phase exception:" indent puts_line "#{error.message}" error.backtrace.each do |l| puts_line l end end def control_example_success(control_group_name, example_data) @successful_audits += 1 end def control_example_failure(control_group_name, example_data, error) @failed_audits += 1 end # Called before action is executed on a resource. def resource_action_start(resource, action, notification_type=nil, notifier=nil) if resource.cookbook_name && resource.recipe_name resource_recipe = "#{resource.cookbook_name}::#{resource.recipe_name}" else resource_recipe = "" end if resource_recipe != @current_recipe && !resource.enclosing_provider unindent if @current_recipe puts_line "Recipe: #{resource_recipe}" @current_recipe = resource_recipe indent end # TODO: info about notifies start_line "* #{resource} action #{action}", :stream => resource indent end # Called when a resource fails, but will retry. def resource_failed_retriable(resource, action, retry_count, exception) end # Called when a resource fails and will not be retried. def resource_failed(resource, action, exception) super unindent end # Called when a resource action has been skipped b/c of a conditional def resource_skipped(resource, action, conditional) # TODO: more info about conditional puts " (skipped due to #{conditional.short_description})", :stream => resource unindent end # Called after #load_current_resource has run. def resource_current_state_loaded(resource, action, current_resource) end # Called when a resource has no converge actions, e.g., it was already correct. def resource_up_to_date(resource, action) @up_to_date_resources+= 1 puts " (up to date)", :stream => resource unindent end def resource_bypassed(resource, action, provider) puts " (Skipped: whyrun not supported by provider #{provider.class.name})", :stream => resource unindent end def output_record(line) end # Called when a change has been made to a resource. May be called multiple # times per resource, e.g., a file may have its content updated, and then # its permissions updated. def resource_update_applied(resource, action, update) prefix = Chef::Config[:why_run] ? "Would " : "" Array(update).each do |line| next if line.nil? output_record line if line.kind_of? String start_line "- #{prefix}#{line}", :green elsif line.kind_of? Array # Expanded output - delta # @todo should we have a resource_update_delta callback? line.each do |detail| start_line detail, :white end end end end # Called after a resource has been completely converged. def resource_updated(resource, action) @updated_resources += 1 unindent puts "\n" end # Called when resource current state load is skipped due to the provider # not supporting whyrun mode. def resource_current_state_load_bypassed(resource, action, current_resource) puts_line("* Whyrun not supported for #{resource}, bypassing load.", :yellow) end def stream_output(stream, output, options = {}) print(output, { :stream => stream }.merge(options)) end # Called before handlers run def handlers_start(handler_count) puts '' puts "Running handlers:" indent end # Called after an individual handler has run def handler_executed(handler) puts_line "- #{handler.class.name}" end # Called after all handlers have executed def handlers_completed unindent puts_line "Running handlers complete\n" end # Called when a provider makes an assumption after a failed assertion # in whyrun mode, in order to allow execution to continue def whyrun_assumption(action, resource, message) return unless message [ message ].flatten.each do |line| start_line("* #{line}", :yellow) end end # Called when an assertion declared by a provider fails def provider_requirement_failed(action, resource, exception, message) return unless message color = Chef::Config[:why_run] ? :yellow : :red [ message ].flatten.each do |line| start_line("* #{line}", color) end end def indent indent_by(2) end def unindent indent_by(-2) end end end end chef-12.3.0/lib/chef/formatters/error_inspectors.rb0000644000004100000410000000141212520074675022330 0ustar www-datawww-datarequire 'chef/formatters/error_inspectors/node_load_error_inspector' require "chef/formatters/error_inspectors/registration_error_inspector" require 'chef/formatters/error_inspectors/compile_error_inspector' require 'chef/formatters/error_inspectors/resource_failure_inspector' require 'chef/formatters/error_inspectors/run_list_expansion_error_inspector' require 'chef/formatters/error_inspectors/cookbook_resolve_error_inspector' require "chef/formatters/error_inspectors/cookbook_sync_error_inspector" class Chef module Formatters # == ErrorInspectors # Error inspectors wrap exceptions and contextual information. They # generate diagnostic messages about possible causes of the error for user # consumption. module ErrorInspectors end end end chef-12.3.0/lib/chef/formatters/minimal.rb0000644000004100000410000001564712520074675020373 0ustar www-datawww-datarequire 'chef/formatters/base' class Chef module Formatters # == Formatters::Minimal # Shows the progress of the chef run by printing single characters, and # displays a summary of updates at the conclusion of the run. For events # that don't have meaningful status information (loading a file, syncing a # cookbook) a dot is printed. For resources, a dot, 'S' or 'U' is printed # if the resource is up to date, skipped by not_if/only_if, or updated, # respectively. class Minimal < Formatters::Base cli_name(:minimal) cli_name(:min) attr_reader :updated_resources attr_reader :updates_by_resource def initialize(out, err) super @updated_resources = [] @updates_by_resource = Hash.new {|h, k| h[k] = []} end # Called at the very start of a Chef Run def run_start(version) puts "Starting Chef Client, version #{version}" end # Called at the end of the Chef run. def run_completed(node) puts "chef client finished, #{@updated_resources.size} resources updated" end # called at the end of a failed run def run_failed(exception) puts "chef client failed. #{@updated_resources.size} resources updated" end # Called right after ohai runs. def ohai_completed(node) end # Already have a client key, assuming this node has registered. def skipping_registration(node_name, config) end # About to attempt to register as +node_name+ def registration_start(node_name, config) end def registration_completed end # Failed to register this client with the server. def registration_failed(node_name, exception, config) super end def node_load_start(node_name, config) end # Failed to load node data from the server def node_load_failed(node_name, exception, config) end # Default and override attrs from roles have been computed, but not yet applied. # Normal attrs from JSON have been added to the node. def node_load_completed(node, expanded_run_list, config) end # Called before the cookbook collection is fetched from the server. def cookbook_resolution_start(expanded_run_list) puts "resolving cookbooks for run list: #{expanded_run_list.inspect}" end # Called when there is an error getting the cookbook collection from the # server. def cookbook_resolution_failed(expanded_run_list, exception) end # Called when the cookbook collection is returned from the server. def cookbook_resolution_complete(cookbook_collection) end # Called before unneeded cookbooks are removed #-- # TODO: Should be called in CookbookVersion.sync_cookbooks def cookbook_clean_start end # Called after the file at +path+ is removed. It may be removed if the # cookbook containing it was removed from the run list, or if the file was # removed from the cookbook. def removed_cookbook_file(path) end # Called when cookbook cleaning is finished. def cookbook_clean_complete end # Called before cookbook sync starts def cookbook_sync_start(cookbook_count) puts "Synchronizing cookbooks" end # Called when cookbook +cookbook_name+ has been sync'd def synchronized_cookbook(cookbook_name) print "." end # Called when an individual file in a cookbook has been updated def updated_cookbook_file(cookbook_name, path) end # Called after all cookbooks have been sync'd. def cookbook_sync_complete puts "done." end # Called when cookbook loading starts. def library_load_start(file_count) puts "Compiling cookbooks" end # Called after a file in a cookbook is loaded. def file_loaded(path) print '.' end def file_load_failed(path, exception) super end # Called when recipes have been loaded. def recipe_load_complete puts "done." end # Called before convergence starts def converge_start(run_context) puts "Converging #{run_context.resource_collection.all_resources.size} resources" end # Called when the converge phase is finished. def converge_complete puts "\n" puts "System converged." if updated_resources.empty? puts "no resources updated" else puts "\n" puts "resources updated this run:" updated_resources.each do |resource| puts "* #{resource.to_s}" updates_by_resource[resource.name].flatten.each do |update| puts " - #{update}" end puts "\n" end end end # Called before action is executed on a resource. def resource_action_start(resource, action, notification_type=nil, notifier=nil) end # Called when a resource fails, but will retry. def resource_failed_retriable(resource, action, retry_count, exception) end # Called when a resource fails and will not be retried. def resource_failed(resource, action, exception) end # Called when a resource action has been skipped b/c of a conditional def resource_skipped(resource, action, conditional) print "S" end # Called after #load_current_resource has run. def resource_current_state_loaded(resource, action, current_resource) end # Called when a resource has no converge actions, e.g., it was already correct. def resource_up_to_date(resource, action) print "." end ## TODO: callback for assertion failures ## TODO: callback for assertion fallback in why run # Called when a change has been made to a resource. May be called multiple # times per resource, e.g., a file may have its content updated, and then # its permissions updated. def resource_update_applied(resource, action, update) @updates_by_resource[resource.name] << Array(update)[0] end # Called after a resource has been completely converged. def resource_updated(resource, action) updated_resources << resource print "U" end # Called before handlers run def handlers_start(handler_count) end # Called after an individual handler has run def handler_executed(handler) end # Called after all handlers have executed def handlers_completed end # An uncategorized message. This supports the case that a user needs to # pass output that doesn't fit into one of the callbacks above. Note that # there's no semantic information about the content or importance of the # message. That means that if you're using this too often, you should add a # callback for it. def msg(message) end end end end chef-12.3.0/lib/chef/formatters/error_descriptor.rb0000644000004100000410000000313712520074675022323 0ustar www-datawww-data# # Author:: Tyler Cloke () # # 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. # class Chef module Formatters # == Formatters::ErrorDescription # Class for displaying errors on STDOUT. class ErrorDescription attr_reader :sections def initialize(title) @title = title @sections = [] end def section(heading, text) @sections << {heading => (text or "")} end def display(out) out.puts "=" * 80 out.puts @title, :red out.puts "=" * 80 out.puts "\n" sections.each do |section| section.each do |heading, text| display_section(heading, text, out) end end end def for_json() { 'title' => @title, 'sections' => @sections } end private def display_section(heading, text, out) out.puts heading out.puts "-" * heading.size out.puts text out.puts "\n" end end end end chef-12.3.0/lib/chef/formatters/indentable_output_stream.rb0000644000004100000410000001142712520074675024035 0ustar www-datawww-dataclass Chef module Formatters # Handles basic indentation and colorization tasks class IndentableOutputStream attr_reader :out attr_reader :err attr_accessor :indent attr_reader :line_started attr_accessor :current_stream attr_reader :semaphore def initialize(out, err) @out, @err = out, err @indent = 0 @line_started = false @semaphore = Mutex.new end def highline @highline ||= begin require 'highline' HighLine.new end end # Print text. This will start a new line and indent if necessary # but will not terminate the line (future print and puts statements # will start off where this print left off). def color(string, *args) print(string, from_args(args)) end # Print the start of a new line. This will terminate any existing lines and # cause indentation but will not move to the next line yet (future 'print' # and 'puts' statements will stay on this line). def start_line(string, *args) print(string, from_args(args, :start_line => true)) end # Print a line. This will continue from the last start_line or print, # or start a new line and indent if necessary. def puts(string, *args) print(string, from_args(args, :end_line => true)) end # Print an entire line from start to end. This will terminate any existing # lines and cause indentation. def puts_line(string, *args) print(string, from_args(args, :start_line => true, :end_line => true)) end # Print a string. # # == Arguments # string: string to print. # options: a hash with these possible options: # - :stream => OBJ: unique identifier for a stream. If two prints have # different streams, they will print on separate lines. # Otherwise, they will stay together. # - :start_line => BOOLEAN: if true, print will begin on a blank (indented) line. # - :end_line => BOOLEAN: if true, current line will be ended. # - :name => STRING: a name to prefix in front of a stream. It will be printed # once (with the first line of the stream) and subsequent lines # will be indented to match. # # == Alternative # # You may also call print('string', :red) (a list of colors a la Highline.color) def print(string, *args) options = from_args(args) # Make sure each line stays a unit even with threads sending output semaphore.synchronize do if should_start_line?(options) move_to_next_line end print_string(string, options) if should_end_line?(options) move_to_next_line end end end private def should_start_line?(options) options[:start_line] || @current_stream != options[:stream] end def should_end_line?(options) options[:end_line] && @line_started end def from_args(colors, merge_options = {}) if colors.size == 1 && colors[0].kind_of?(Hash) merge_options.merge(colors[0]) else merge_options.merge({ :colors => colors }) end end def print_string(string, options) if string.empty? if options[:end_line] print_line('', options) end else string.lines.each do |line| print_line(line, options) end end end def print_line(line, options) indent_line(options) # Note that the next line will need to be started if line[-1..-1] == "\n" @line_started = false end if Chef::Config[:color] && options[:colors] @out.print highline.color(line, *options[:colors]) else @out.print line end end def move_to_next_line if @line_started @out.puts '' @line_started = false end end def indent_line(options) if !@line_started # Print indents. If there is a stream name, either print it (if we're # switching streams) or print enough blanks to match # the indents. if options[:name] if @current_stream != options[:stream] @out.print "#{(' ' * indent)}[#{options[:name]}] " else @out.print ' ' * (indent + 3 + options[:name].size) end else # Otherwise, just print indents. @out.print ' ' * indent end if @current_stream != options[:stream] @current_stream = options[:stream] end @line_started = true end end end end end chef-12.3.0/lib/chef/formatters/error_mapper.rb0000644000004100000410000000705512520074675021434 0ustar www-datawww-data#-- # Author:: Tyler Cloke () # 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. # class Chef module Formatters # == Formatters::ErrorMapper # Collection of methods for creating and returning # Formatters::ErrorDescription objects based on node, # exception, and configuration information. module ErrorMapper # Failed to register this client with the server. def self.registration_failed(node_name, exception, config) error_inspector = ErrorInspectors::RegistrationErrorInspector.new(node_name, exception, config) headline = "Chef encountered an error attempting to create the client \"#{node_name}\"" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.node_load_failed(node_name, exception, config) error_inspector = ErrorInspectors::NodeLoadErrorInspector.new(node_name, exception, config) headline = "Chef encountered an error attempting to load the node data for \"#{node_name}\"" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.run_list_expand_failed(node, exception) error_inspector = ErrorInspectors::RunListExpansionErrorInspector.new(node, exception) headline = "Error expanding the run_list:" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.cookbook_resolution_failed(expanded_run_list, exception) error_inspector = ErrorInspectors::CookbookResolveErrorInspector.new(expanded_run_list, exception) headline = "Error Resolving Cookbooks for Run List:" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.cookbook_sync_failed(cookbooks, exception) error_inspector = ErrorInspectors::CookbookSyncErrorInspector.new(cookbooks, exception) headline = "Error Syncing Cookbooks:" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.resource_failed(resource, action, exception) error_inspector = ErrorInspectors::ResourceFailureInspector.new(resource, action, exception) headline = "Error executing action `#{action}` on resource '#{resource}'" description = ErrorDescription.new(headline) error_inspector.add_explanation(description) return description end def self.file_load_failed(path, exception) error_inspector = ErrorInspectors::CompileErrorInspector.new(path, exception) headline = "Recipe Compile Error" + ( path ? " in #{path}" : "" ) description = ErrorDescription.new(headline) error_inspector.add_explanation(description) description end end end end chef-12.3.0/lib/chef/formatters/base.rb0000644000004100000410000001503712520074675017650 0ustar www-datawww-data# # Author:: Tyler Cloke () # # 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/event_dispatch/base' require 'chef/formatters/error_inspectors' require 'chef/formatters/error_descriptor' require 'chef/formatters/error_mapper' require 'chef/formatters/indentable_output_stream' class Chef # == Chef::Formatters # Formatters handle printing output about the progress/status of a chef # client run to the user's screen. module Formatters class UnknownFormatter < StandardError; end def self.formatters_by_name @formatters_by_name ||= {} end def self.register(name, formatter) formatters_by_name[name.to_s] = formatter end def self.by_name(name) formatters_by_name[name] end def self.available_formatters formatters_by_name.keys end #-- # TODO: is it too clever to be defining new() on a module like this? def self.new(name, out, err) formatter_class = by_name(name.to_s) or raise UnknownFormatter, "No output formatter found for #{name} (available: #{available_formatters.join(', ')})" formatter_class.new(out, err) end # == Formatters::Base # Base class that all formatters should inherit from. class Base < EventDispatch::Base include ErrorMapper def self.cli_name(name) Chef::Formatters.register(name, self) end attr_reader :out attr_reader :err attr_reader :output def initialize(out, err) @output = IndentableOutputStream.new(out, err) end def puts(*args) @output.puts(*args) end def print(*args) @output.print(*args) end def puts_line(*args) @output.puts_line(*args) end def start_line(*args) @output.start_line(*args) end def indent_by(amount) @output.indent += amount if @output.indent < 0 # This is left commented out for now. We need to uncomment it and fix at least one bug in # the formatter, and then leave this line uncommented in the future. #Chef::Log.warn "Internal Formatter Error -- Attempt to indent by negative number of spaces" @output.indent = 0 end @output.indent end # Input: a Formatters::ErrorDescription object. # Outputs error to STDOUT. def display_error(description) puts("") description.display(output) end def registration_failed(node_name, exception, config) #A Formatters::ErrorDescription object description = ErrorMapper.registration_failed(node_name, exception, config) display_error(description) end def node_load_failed(node_name, exception, config) description = ErrorMapper.node_load_failed(node_name, exception, config) display_error(description) end def run_list_expand_failed(node, exception) description = ErrorMapper.run_list_expand_failed(node, exception) display_error(description) end def cookbook_resolution_failed(expanded_run_list, exception) description = ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception) display_error(description) end def cookbook_sync_failed(cookbooks, exception) description = ErrorMapper.cookbook_sync_failed(cookbooks, exception) display_error(description) end def resource_failed(resource, action, exception) description = ErrorMapper.resource_failed(resource, action, exception) display_error(description) end # Generic callback for any attribute/library/lwrp/recipe file in a # cookbook getting loaded. The per-filetype callbacks for file load are # overriden so that they call this instead. This means that a subclass of # Formatters::Base can implement #file_loaded to do the same thing for # every kind of file that Chef loads from a recipe instead of # implementing all the per-filetype callbacks. def file_loaded(path) end # Generic callback for any attribute/library/lwrp/recipe file throwing an # exception when loaded. Default behavior is to use CompileErrorInspector # to print contextual info about the failure. def file_load_failed(path, exception) description = ErrorMapper.file_load_failed(path, exception) display_error(description) end def recipe_not_found(exception) description = ErrorMapper.file_load_failed(nil, exception) display_error(description) end # Delegates to #file_loaded def library_file_loaded(path) file_loaded(path) end # Delegates to #file_load_failed def library_file_load_failed(path, exception) file_load_failed(path, exception) end # Delegates to #file_loaded def lwrp_file_loaded(path) file_loaded(path) end # Delegates to #file_load_failed def lwrp_file_load_failed(path, exception) file_load_failed(path, exception) end # Delegates to #file_loaded def attribute_file_loaded(path) file_loaded(path) end # Delegates to #file_load_failed def attribute_file_load_failed(path, exception) file_load_failed(path, exception) end # Delegates to #file_loaded def definition_file_loaded(path) file_loaded(path) end # Delegates to #file_load_failed def definition_file_load_failed(path, exception) file_load_failed(path, exception) end # Delegates to #file_loaded def recipe_file_loaded(path) file_loaded(path) end # Delegates to #file_load_failed def recipe_file_load_failed(path, exception) file_load_failed(path, exception) end end # == NullFormatter # Formatter that doesn't actually produce any output. You can use this to # disable the use of output formatters. class NullFormatter < Base cli_name(:null) end end end chef-12.3.0/lib/chef/formatters/error_inspectors/0000755000004100000410000000000012520074675022005 5ustar www-datawww-datachef-12.3.0/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb0000644000004100000410000000774412520074675027772 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # 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. # class Chef module Formatters module ErrorInspectors class ResourceFailureInspector attr_reader :resource attr_reader :action attr_reader :exception def initialize(resource, action, exception) @resource = resource @action = action @exception = exception end def add_explanation(error_description) error_description.section(exception.class.name, exception.message) unless filtered_bt.empty? error_description.section("Cookbook Trace:", filtered_bt.join("\n")) end unless dynamic_resource? error_description.section("Resource Declaration:", resource.sensitive ? "suppressed sensitive resource output" : recipe_snippet) end error_description.section("Compiled Resource:", "#{resource.to_text}") # Template errors get wrapped in an exception class that can show the relevant template code, # so add them to the error output. if exception.respond_to?(:source_listing) error_description.section("Template Context:", "#{exception.source_location}\n#{exception.source_listing}") end if Chef::Platform.windows? require 'chef/win32/security' if !Chef::ReservedNames::Win32::Security.has_admin_privileges? error_description.section("Missing Windows Admin Privileges", "chef-client doesn't have administrator privileges. This can be a possible reason for the resource failure.") end end end def recipe_snippet return nil if dynamic_resource? @snippet ||= begin if file = resource.source_line[/^(([\w]:)?[^:]+):([\d]+)/,1] and line = resource.source_line[/^#{file}:([\d]+)/,1].to_i return nil unless ::File.exists?(file) lines = IO.readlines(file) relevant_lines = ["# In #{file}\n\n"] current_line = line - 1 current_line = 0 if current_line < 0 nesting = 0 loop do # low rent parser. try to gracefully handle nested blocks in resources nesting += 1 if lines[current_line] =~ /[\s]+do[\s]*/ nesting -= 1 if lines[current_line] =~ /end[\s]*$/ relevant_lines << format_line(current_line, lines[current_line]) break if lines[current_line + 1].nil? break if current_line >= (line + 50) break if nesting <= 0 current_line += 1 end relevant_lines << format_line(current_line + 1, lines[current_line + 1]) if lines[current_line + 1] relevant_lines.join("") end end end def dynamic_resource? !resource.source_line end def filtered_bt filters = Array(Chef::Config.cookbook_path).map {|p| /^#{Regexp.escape(p)}/ } exception.backtrace.select {|line| filters.any? {|filter| line =~ filter }} end private def format_line(line_nr, line) # Print line number as 1-indexed not zero line_nr_string = (line_nr + 1).to_s.rjust(3) + ": " line_nr_string + line end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/registration_error_inspector.rb0000644000004100000410000001205612520074675030347 0ustar www-datawww-dataclass Chef module Formatters module ErrorInspectors # == RegistrationErrorInspector # Wraps exceptions that occur during the client registration process and # suggests possible causes. #-- # TODO: Lots of duplication with the node_load_error_inspector, just # slightly tweaked to talk about validation keys instead of other keys. class RegistrationErrorInspector attr_reader :exception attr_reader :node_name attr_reader :config def initialize(node_name, exception, config) @node_name = node_name @exception = exception @config = config end def add_explanation(error_description) case exception when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(error_description) when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError error_description.section("Network Error:",<<-E) There was a network error connecting to the Chef Server: #{exception.message} E error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" If your chef_server_url is correct, your network could be down. E when Chef::Exceptions::PrivateKeyMissing error_description.section("Private Key Not Found:",<<-E) Your private key could not be loaded. If the key file exists, ensure that it is readable by chef-client. E error_description.section("Relevant Config Settings:",<<-E) validation_key "#{api_key}" E when Chef::Exceptions::InvalidRedirect error_description.section("Invalid Redirect:",<<-E) Change your server location in client.rb to the server's FQDN to avoid unwanted redirections. E else "#{exception.class.name}: #{exception.message}" end end def humanize_http_exception(error_description) response = exception.response case response when Net::HTTPUnauthorized if clock_skew? error_description.section("Authentication Error:",<<-E) Failed to authenticate to the chef server (http 401). The request failed because your clock has drifted by more than 15 minutes. Syncing your clock to an NTP Time source should resolve the issue. E else error_description.section("Authentication Error:",<<-E) Failed to authenticate to the chef server (http 401). E error_description.section("Server Response:", format_rest_error) error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" validation_client_name "#{username}" validation_key "#{api_key}" If these settings are correct, your validation_key may be invalid. E end when Net::HTTPForbidden error_description.section("Authorization Error:",<<-E) Your validation client is not authorized to create the client for this node (HTTP 403). E error_description.section("Possible Causes:",<<-E) * There may already be a client named "#{config[:node_name]}" * Your validation client (#{username}) may have misconfigured authorization permissions. E when Net::HTTPBadRequest error_description.section("Invalid Request Data:",<<-E) The data in your request was invalid (HTTP 400). E error_description.section("Server Response:",format_rest_error) when Net::HTTPNotFound error_description.section("Resource Not Found:",<<-E) The server returned a HTTP 404. This usually indicates that your chef_server_url is incorrect. E error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" E when Net::HTTPInternalServerError error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load the node data. E error_description.section("Server Response:", format_rest_error) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable error_description.section("Server Unavailable","The Chef Server is temporarily unavailable") error_description.section("Server Response:", format_rest_error) else error_description.section("Unexpected API Request Failure:", format_rest_error) end end def username #config[:node_name] config[:validation_client_name] end def api_key config[:validation_key] #config[:client_key] end def server_url config[:chef_server_url] end def clock_skew? exception.response.body =~ /synchronize the clock/i end # Parses JSON from the error response sent by Chef Server and returns the # error message #-- # TODO: this code belongs in Chef::REST def format_rest_error Array(Chef::JSONCompat.from_json(exception.response.body)["error"]).join('; ') rescue Exception exception.response.body end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/compile_error_inspector.rb0000644000004100000410000000657512520074675027276 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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. # class Chef module Formatters module ErrorInspectors # == CompileErrorInspector # Wraps exceptions that occur during the compile phase of a Chef run and # tries to find the code responsible for the error. class CompileErrorInspector attr_reader :path attr_reader :exception def initialize(path, exception) @path, @exception = path, exception end def add_explanation(error_description) case exception when Chef::Exceptions::RecipeNotFound error_description.section(exception.class.name, exception.message) else error_description.section(exception.class.name, exception.message) traceback = filtered_bt.map {|line| " #{line}"}.join("\n") error_description.section("Cookbook Trace:", traceback) error_description.section("Relevant File Content:", context) end end def context context_lines = [] context_lines << "#{culprit_file}:\n\n" Range.new(display_lower_bound, display_upper_bound).each do |i| line_nr = (i + 1).to_s.rjust(3) indicator = (i + 1) == culprit_line ? ">> " : ": " context_lines << "#{line_nr}#{indicator}#{file_lines[i]}" end context_lines.join("") end def display_lower_bound lower = (culprit_line - 8) lower = 0 if lower < 0 lower end def display_upper_bound upper = (culprit_line + 8) upper = file_lines.size if upper > file_lines.size upper end def file_lines @file_lines ||= IO.readlines(culprit_file) end def culprit_backtrace_entry @culprit_backtrace_entry ||= begin bt_entry = filtered_bt.first Chef::Log.debug("backtrace entry for compile error: '#{bt_entry}'") bt_entry end end def culprit_line @culprit_line ||= begin line_number = culprit_backtrace_entry[/^(?:.\:)?[^:]+:([\d]+)/,1].to_i Chef::Log.debug("Line number of compile error: '#{line_number}'") line_number end end def culprit_file @culprit_file ||= culprit_backtrace_entry[/^((?:.\:)?[^:]+):([\d]+)/,1] end def filtered_bt filters = Array(Chef::Config.cookbook_path).map {|p| /^#{Regexp.escape(p)}/ } r = exception.backtrace.select {|line| filters.any? {|filter| line =~ filter }} Chef::Log.debug("filtered backtrace of compile error: #{r.join(",")}") return r.count > 0 ? r : exception.backtrace end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb0000644000004100000410000001015312520074675027555 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/formatters/error_inspectors/api_error_formatting' class Chef module Formatters module ErrorInspectors # == APIErrorInspector # Wraps exceptions caused by API calls to the server. class NodeLoadErrorInspector include APIErrorFormatting attr_reader :exception attr_reader :node_name attr_reader :config def initialize(node_name, exception, config) @node_name = node_name @exception = exception @config = config end def add_explanation(error_description) case exception when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(error_description) when *NETWORK_ERROR_CLASSES describe_network_errors(error_description) when Chef::Exceptions::PrivateKeyMissing error_description.section("Private Key Not Found:",<<-E) Your private key could not be loaded. If the key file exists, ensure that it is readable by chef-client. E error_description.section("Relevant Config Settings:",<<-E) client_key "#{api_key}" E else error_description.section("Unexpected Error:","#{exception.class.name}: #{exception.message}") end end def humanize_http_exception(error_description) response = exception.response case response when Net::HTTPUnauthorized # TODO: this is where you'd see conflicts b/c of username/clientname stuff describe_401_error(error_description) when Net::HTTPForbidden # TODO: we're rescuing errors from Node.find_or_create # * could be no write on nodes container # * could be no read on the node error_description.section("Authorization Error",<<-E) Your client is not authorized to load the node data (HTTP 403). E error_description.section("Server Response:", format_rest_error) error_description.section("Possible Causes:",<<-E) * Your client (#{username}) may have misconfigured authorization permissions. E when Net::HTTPBadRequest describe_400_error(error_description) when Net::HTTPNotFound describe_404_error(error_description) when Net::HTTPInternalServerError describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable describe_503_error(error_description) else describe_http_error(error_description) end end # Custom 404 error messaging. Users sometimes see 404s when they have # misconfigured server URLs, and the wrong one redirects to the new # one, e.g., PUT http://wrong.url/nodes/node-name becomes a GET after a # redirect. def describe_404_error(error_description) error_description.section("Resource Not Found:",<<-E) The server returned a HTTP 404. This usually indicates that your chef_server_url is incorrect. E error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" E end def username config[:node_name] end def api_key config[:client_key] end def server_url config[:chef_server_url] end def clock_skew? exception.response.body =~ /synchronize the clock/i end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/api_error_formatting.rb0000644000004100000410000001020712520074675026546 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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. # class Chef module Formatters module APIErrorFormatting NETWORK_ERROR_CLASSES = [Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError] def describe_network_errors(error_description) error_description.section("Networking Error:",<<-E) #{exception.message} Your chef_server_url may be misconfigured, or the network could be down. E error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" E end def describe_401_error(error_description) if clock_skew? error_description.section("Authentication Error:",<<-E) Failed to authenticate to the chef server (http 401). The request failed because your clock has drifted by more than 15 minutes. Syncing your clock to an NTP Time source should resolve the issue. E else error_description.section("Authentication Error:",<<-E) Failed to authenticate to the chef server (http 401). E error_description.section("Server Response:", format_rest_error) error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" node_name "#{username}" client_key "#{api_key}" If these settings are correct, your client_key may be invalid, or you may have a chef user with the same client name as this node. E end end def describe_400_error(error_description) error_description.section("Invalid Request Data:",<<-E) The data in your request was invalid (HTTP 400). E error_description.section("Server Response:",format_rest_error) end def describe_500_error(error_description) error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load the node data. E error_description.section("Server Response:", format_rest_error) end def describe_503_error(error_description) error_description.section("Server Unavailable","The Chef Server is temporarily unavailable") error_description.section("Server Response:", format_rest_error) end # Fallback for unexpected/uncommon http errors def describe_http_error(error_description) error_description.section("Unexpected API Request Failure:", format_rest_error) end # Parses JSON from the error response sent by Chef Server and returns the # error message def format_rest_error Array(Chef::JSONCompat.from_json(exception.response.body)["error"]).join('; ') rescue Exception safe_format_rest_error end def username config[:node_name] end def api_key config[:client_key] end def server_url config[:chef_server_url] end def clock_skew? exception.response.body =~ /synchronize the clock/i end def safe_format_rest_error # When we get 504 from the server, sometimes the response body is non-readable. # # Stack trace: # # NoMethodError: undefined method `closed?' for nil:NilClass # .../lib/ruby/1.9.1/net/http.rb:2789:in `stream_check' # .../lib/ruby/1.9.1/net/http.rb:2709:in `read_body' # .../lib/ruby/1.9.1/net/http.rb:2736:in `body' # .../lib/chef/formatters/error_inspectors/api_error_formatting.rb:91:in `rescue in format_rest_error' begin exception.response.body rescue Exception "Cannot fetch the contents of the response." end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb0000644000004100000410000001021312520074675031551 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # 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/formatters/error_inspectors/api_error_formatting' class Chef module Formatters module ErrorInspectors class RunListExpansionErrorInspector include APIErrorFormatting attr_reader :exception attr_reader :node def initialize(node, exception) @node, @exception = node, exception end def add_explanation(error_description) case exception when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError error_description.section("Networking Error:",<<-E) #{exception.message} Your chef_server_url may be misconfigured, or the network could be down. E when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(error_description) when Chef::Exceptions::MissingRole describe_missing_role(error_description) else error_description.section("Unexpected Error:","#{exception.class.name}: #{exception.message}") end end def describe_missing_role(error_description) error_description.section("Missing Role(s) in Run List:", missing_roles_explained) original_run_list = node.run_list.map {|item| "* #{item}"}.join("\n") error_description.section("Original Run List", original_run_list) end def missing_roles_explained run_list_expansion.missing_roles_with_including_role.map do |role, includer| "* #{role} included by '#{includer}'" end.join("\n") end def run_list_expansion exception.expansion end def config Chef::Config end def humanize_http_exception(error_description) response = exception.response case response when Net::HTTPUnauthorized error_description.section("Authentication Error:",<<-E) Failed to authenticate to the chef server (http 401). E error_description.section("Server Response:", format_rest_error) error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" node_name "#{username}" client_key "#{api_key}" If these settings are correct, your client_key may be invalid. E when Net::HTTPForbidden # TODO: we're rescuing errors from Node.find_or_create # * could be no write on nodes container # * could be no read on the node error_description.section("Authorization Error",<<-E) Your client is not authorized to load one or more of your roles (HTTP 403). E error_description.section("Server Response:", format_rest_error) error_description.section("Possible Causes:",<<-E) * Your client (#{username}) may have misconfigured authorization permissions. E when Net::HTTPInternalServerError error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load a role. E error_description.section("Server Response:", format_rest_error) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable error_description.section("Server Unavailable","The Chef Server is temporarily unavailable") error_description.section("Server Response:", format_rest_error) else error_description.section("Unexpected API Request Failure:", format_rest_error) end end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb0000644000004100000410000000502012520074675030470 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/formatters/error_inspectors/api_error_formatting' class Chef module Formatters module ErrorInspectors # == CookbookSyncErrorInspector # Generates human-friendly explanations for errors encountered during # cookbook sync. #-- # TODO: Not sure what errors are commonly seen during cookbook sync, so # the messaging is kinda generic. class CookbookSyncErrorInspector include APIErrorFormatting attr_reader :exception attr_reader :cookbooks def initialize(cookbooks, exception) @cookbooks, @exception = cookbooks, exception end def add_explanation(error_description) case exception when *NETWORK_ERROR_CLASSES describe_network_errors(error_description) when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(error_description) else error_description.section("Unexpected Error:","#{exception.class.name}: #{exception.message}") end end def config Chef::Config end def humanize_http_exception(error_description) response = exception.response case response when Net::HTTPUnauthorized # TODO: this is where you'd see conflicts b/c of username/clientname stuff describe_401_error(error_description) when Net::HTTPBadRequest describe_400_error(error_description) when Net::HTTPNotFound when Net::HTTPInternalServerError describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable, Net::HTTPGatewayTimeOut describe_503_error(error_description) else describe_http_error(error_description) end end end end end end chef-12.3.0/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb0000644000004100000410000001462512520074675031206 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/formatters/error_inspectors/api_error_formatting' class Chef module Formatters module ErrorInspectors class CookbookResolveErrorInspector attr_reader :exception attr_reader :expanded_run_list include APIErrorFormatting def initialize(expanded_run_list, exception) @expanded_run_list = expanded_run_list @exception = exception end def add_explanation(error_description) case exception when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(error_description) when *NETWORK_ERROR_CLASSES describe_network_errors(error_description) else error_description.section("Unexpected Error:","#{exception.class.name}: #{exception.message}") end end def humanize_http_exception(error_description) response = exception.response case response when Net::HTTPUnauthorized # TODO: this is where you'd see conflicts b/c of username/clientname stuff describe_401_error(error_description) when Net::HTTPForbidden # TODO: we're rescuing errors from Node.find_or_create # * could be no write on nodes container # * could be no read on the node error_description.section("Authorization Error",<<-E) This client is not authorized to read some of the information required to access its cookbooks (HTTP 403). To access its cookbooks, a client needs to be able to read its environment and all of the cookbooks in its expanded run list. E error_description.section("Expanded Run List:", expanded_run_list_ul) error_description.section("Server Response:", format_rest_error) when Net::HTTPPreconditionFailed describe_412_error(error_description) when Net::HTTPBadRequest describe_400_error(error_description) when Net::HTTPNotFound when Net::HTTPInternalServerError describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable describe_503_error(error_description) else describe_http_error(error_description) end end def describe_412_error(error_description) explanation = "" error_reasons = extract_412_error_message # Prepare the error message if there is detailed information # about individual cookbooks. if !error_reasons.respond_to?(:key?) explanation << error_reasons.to_s else if error_reasons.key?("non_existent_cookbooks") && !Array(error_reasons["non_existent_cookbooks"]).empty? explanation << "The following cookbooks are required by the client but don't exist on the server:\n" Array(error_reasons["non_existent_cookbooks"]).each do |cookbook| explanation << "* #{cookbook}\n" end explanation << "\n" end if error_reasons.key?("cookbooks_with_no_versions") && !Array(error_reasons["cookbooks_with_no_versions"]).empty? explanation << "The following cookbooks exist on the server, but there is no version that meets\nthe version constraints in this environment:\n" Array(error_reasons["cookbooks_with_no_versions"]).each do |cookbook| explanation << "* #{cookbook}\n" end explanation << "\n" end end if !explanation.empty? error_description.section("Missing Cookbooks:", explanation) else # If we don't have any cookbook details print a more # generic error message. if error_reasons.respond_to?(:key?) && error_reasons["message"] explanation << "Error message: #{error_reasons["message"]}\n" end explanation << < ["nope"], # "cookbooks_with_no_versions" => [], # "message" => "Run list contains invalid items: no such cookbook nope."} def extract_412_error_message # Example: # "{\"error\":[\"{\\\"non_existent_cookbooks\\\":[\\\"nope\\\"],\\\"cookbooks_with_no_versions\\\":[],\\\"message\\\":\\\"Run list contains invalid items: no such cookbook nope.\\\"}\"]}" wrapped_error_message = attempt_json_parse(exception.response.body) unless wrapped_error_message.kind_of?(Hash) && wrapped_error_message.key?("error") return wrapped_error_message.to_s end error_description = Array(wrapped_error_message["error"]).first if error_description.kind_of?(Hash) return error_description end attempt_json_parse(error_description) end private def attempt_json_parse(maybe_json_string) Chef::JSONCompat.from_json(maybe_json_string) rescue Exception maybe_json_string end end end end end chef-12.3.0/lib/chef/handler.rb0000644000004100000410000001613712520074675016167 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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/client' require 'forwardable' class Chef # == Chef::Handler # The base class for an Exception or Notification Handler. Create your own # handler by subclassing Chef::Handler. When a Chef run fails with an # uncaught Exception, Chef will set the +run_status+ on your handler and call # +report+ # # ===Example: # # require 'net/smtp' # # module MyOrg # class OhNoes < Chef::Handler # # def report # # Create the email message # message = "From: Your Name \n" # message << "To: Destination Address \n" # message << "Subject: Chef Run Failure\n" # message << "Date: #{Time.now.rfc2822}\n\n" # # # The Node is available as +node+ # message << "Chef run failed on #{node.name}\n" # # +run_status+ is a value object with all of the run status data # message << "#{run_status.formatted_exception}\n" # # Join the backtrace lines. Coerce to an array just in case. # message << Array(backtrace).join("\n") # # # Send the email # Net::SMTP.start('your.smtp.server', 25) do |smtp| # smtp.send_message message, 'from@address', 'to@address' # end # end # # end # end # class Handler # The list of currently configured start handlers def self.start_handlers Array(Chef::Config[:start_handlers]) end # Run the start handlers. This will usually be called by a notification # from Chef::Client def self.run_start_handlers(run_status) Chef::Log.info("Running start handlers") start_handlers.each do |handler| handler.run_report_safely(run_status) end Chef::Log.info("Start handlers complete.") end # Wire up a notification to run the start handlers when the chef run # starts. Chef::Client.when_run_starts do |run_status| run_start_handlers(run_status) end # The list of currently configured report handlers def self.report_handlers Array(Chef::Config[:report_handlers]) end # Run the report handlers. This will usually be called by a notification # from Chef::Client def self.run_report_handlers(run_status) events = run_status.events events.handlers_start(report_handlers.size) Chef::Log.info("Running report handlers") report_handlers.each do |handler| handler.run_report_safely(run_status) events.handler_executed(handler) end events.handlers_completed Chef::Log.info("Report handlers complete") end # Wire up a notification to run the report handlers if the chef run # succeeds. Chef::Client.when_run_completes_successfully do |run_status| run_report_handlers(run_status) end # The list of currently configured exception handlers def self.exception_handlers Array(Chef::Config[:exception_handlers]) end # Run the exception handlers. Usually will be called by a notification # from Chef::Client when the run fails. def self.run_exception_handlers(run_status) events = run_status.events events.handlers_start(exception_handlers.size) Chef::Log.error("Running exception handlers") exception_handlers.each do |handler| handler.run_report_safely(run_status) events.handler_executed(handler) end events.handlers_completed Chef::Log.error("Exception handlers complete") end # Wire up a notification to run the exception handlers if the chef run fails. Chef::Client.when_run_fails do |run_status| run_exception_handlers(run_status) end extend Forwardable # The Chef::RunStatus object containing data about the Chef run. attr_reader :run_status ## # :method: start_time # # The time the chef run started def_delegator :@run_status, :start_time ## # :method: end_time # # The time the chef run ended def_delegator :@run_status, :end_time ## # :method: elapsed_time # # The time elapsed between the start and finish of the chef run def_delegator :@run_status, :elapsed_time ## # :method: run_context # # The Chef::RunContext object used by the chef run def_delegator :@run_status, :run_context ## # :method: exception # # The uncaught Exception that terminated the chef run, or nil if the run # completed successfully def_delegator :@run_status, :exception ## # :method: backtrace # # The backtrace captured by the uncaught exception that terminated the chef # run, or nil if the run completed successfully def_delegator :@run_status, :backtrace ## # :method: node # # The Chef::Node for this client run def_delegator :@run_status, :node ## # :method: all_resources # # An Array containing all resources in the chef run's resource_collection def_delegator :@run_status, :all_resources ## # :method: updated_resources # # An Array containing all resources that were updated during the chef run def_delegator :@run_status, :updated_resources ## # :method: success? # # Was the chef run successful? True if the chef run did not raise an # uncaught exception def_delegator :@run_status, :success? ## # :method: failed? # # Did the chef run fail? True if the chef run raised an uncaught exception def_delegator :@run_status, :failed? # The main entry point for report handling. Subclasses should override this # method with their own report handling logic. def report end # Runs the report handler, rescuing and logging any errors it may cause. # This ensures that all handlers get a chance to run even if one fails. # This method should not be overridden by subclasses unless you know what # you're doing. def run_report_safely(run_status) run_report_unsafe(run_status) rescue Exception => e Chef::Log.error("Report handler #{self.class.name} raised #{e.inspect}") Array(e.backtrace).each { |line| Chef::Log.error(line) } ensure @run_status = nil end # Runs the report handler without any error handling. This method should # not be used directly except in testing. def run_report_unsafe(run_status) @run_status = run_status report end # Return the Hash representation of the run_status def data @run_status.to_hash end end end chef-12.3.0/lib/chef/config.rb0000644000004100000410000007716712520074675016031 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # Author:: AJ Christensen () # Author:: Mark Mzyk () # Author:: Kyle Goodwin () # 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 'chef/log' require 'chef/exceptions' require 'mixlib/config' require 'chef/util/selinux' require 'chef/util/path_helper' require 'pathname' require 'chef/mixin/shell_out' class Chef class Config extend Mixlib::Config extend Chef::Mixin::ShellOut PathHelper = Chef::Util::PathHelper # Evaluates the given string as config. # # +filename+ is used for context in stacktraces, but doesn't need to be the name of an actual file. def self.from_string(string, filename) self.instance_eval(string, filename, 1) end # Manages the chef secret session key # === Returns # :: A new or retrieved session key # def self.manage_secret_key newkey = nil if Chef::FileCache.has_key?("chef_server_cookie_id") newkey = Chef::FileCache.load("chef_server_cookie_id") else chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a newkey = "" 40.times { |i| newkey << chars[rand(chars.size-1)] } Chef::FileCache.store("chef_server_cookie_id", newkey) end newkey end def self.inspect configuration.inspect end def self.platform_specific_path(path) path = PathHelper.cleanpath(path) if Chef::Platform.windows? # turns \etc\chef\client.rb and \var\chef\client.rb into C:/chef/client.rb if env['SYSTEMDRIVE'] && path[0] == '\\' && path.split('\\')[2] == 'chef' path = PathHelper.join(env['SYSTEMDRIVE'], path.split('\\', 3)[2]) end end path end def self.add_formatter(name, file_path=nil) formatters << [name, file_path] end def self.add_event_logger(logger) event_handlers << logger end # Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.) configurable(:config_file) default(:config_dir) do if config_file PathHelper.dirname(config_file) else PathHelper.join(user_home, ".chef", "") end end default :formatters, [] # Override the config dispatch to set the value of multiple server options simultaneously # # === Parameters # url:: String to be set for all of the chef-server-api URL's # configurable(:chef_server_url).writes_value { |url| url.to_s.strip } # When you are using ActiveSupport, they monkey-patch 'daemonize' into Kernel. # So while this is basically identical to what method_missing would do, we pull # it up here and get a real method written so that things get dispatched # properly. configurable(:daemonize).writes_value { |v| v } # The root where all local chef object data is stored. cookbooks, data bags, # environments are all assumed to be in separate directories under this. # chef-solo uses these directories for input data. knife commands # that upload or download files (such as knife upload, knife role from file, # etc.) work. default :chef_repo_path do if self.configuration[:cookbook_path] if self.configuration[:cookbook_path].kind_of?(String) File.expand_path('..', self.configuration[:cookbook_path]) else self.configuration[:cookbook_path].map do |path| File.expand_path('..', path) end end else cache_path end end def self.find_chef_repo_path(cwd) # In local mode, we auto-discover the repo root by looking for a path with "cookbooks" under it. # This allows us to run config-free. path = cwd until File.directory?(PathHelper.join(path, "cookbooks")) new_path = File.expand_path('..', path) if new_path == path Chef::Log.warn("No cookbooks directory found at or above current directory. Assuming #{Dir.pwd}.") return Dir.pwd end path = new_path end Chef::Log.info("Auto-discovered chef repository at #{path}") path end def self.derive_path_from_chef_repo_path(child_path) if chef_repo_path.kind_of?(String) PathHelper.join(chef_repo_path, child_path) else chef_repo_path.map { |path| PathHelper.join(path, child_path)} end end # Location of acls on disk. String or array of strings. # Defaults to /acls. # Only applies to Enterprise Chef commands. default(:acl_path) { derive_path_from_chef_repo_path('acls') } # Location of clients on disk. String or array of strings. # Defaults to /acls. default(:client_path) { derive_path_from_chef_repo_path('clients') } # Location of cookbooks on disk. String or array of strings. # Defaults to /cookbooks. If chef_repo_path # is not specified, this is set to [/var/chef/cookbooks, /var/chef/site-cookbooks]). default(:cookbook_path) do if self.configuration[:chef_repo_path] derive_path_from_chef_repo_path('cookbooks') else Array(derive_path_from_chef_repo_path('cookbooks')).flatten + Array(derive_path_from_chef_repo_path('site-cookbooks')).flatten end end # Location of containers on disk. String or array of strings. # Defaults to /containers. # Only applies to Enterprise Chef commands. default(:container_path) { derive_path_from_chef_repo_path('containers') } # Location of data bags on disk. String or array of strings. # Defaults to /data_bags. default(:data_bag_path) { derive_path_from_chef_repo_path('data_bags') } # Location of environments on disk. String or array of strings. # Defaults to /environments. default(:environment_path) { derive_path_from_chef_repo_path('environments') } # Location of groups on disk. String or array of strings. # Defaults to /groups. # Only applies to Enterprise Chef commands. default(:group_path) { derive_path_from_chef_repo_path('groups') } # Location of nodes on disk. String or array of strings. # Defaults to /nodes. default(:node_path) { derive_path_from_chef_repo_path('nodes') } # Location of roles on disk. String or array of strings. # Defaults to /roles. default(:role_path) { derive_path_from_chef_repo_path('roles') } # Location of users on disk. String or array of strings. # Defaults to /users. # Does not apply to Enterprise Chef commands. default(:user_path) { derive_path_from_chef_repo_path('users') } # Location of policies on disk. String or array of strings. # Defaults to /policies. default(:policy_path) { derive_path_from_chef_repo_path('policies') } # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity default :enforce_path_sanity, true # Formatted Chef Client output is a beta feature, disabled by default: default :formatter, "null" # The number of times the client should retry when registering with the server default :client_registration_retries, 5 # An array of paths to search for knife exec scripts if they aren't in the current directory default :script_path, [] # The root of all caches (checksums, cache and backup). If local mode is on, # this is under the user's home directory. default(:cache_path) do if local_mode PathHelper.join(config_dir, 'local-mode-cache') else primary_cache_root = platform_specific_path("/var") primary_cache_path = platform_specific_path("/var/chef") # Use /var/chef as the cache path only if that folder exists and we can read and write # into it, or /var exists and we can read and write into it (we'll create /var/chef later). # Otherwise, we'll create .chef under the user's home directory and use that as # the cache path. unless path_accessible?(primary_cache_path) || path_accessible?(primary_cache_root) secondary_cache_path = PathHelper.join(user_home, '.chef') Chef::Log.info("Unable to access cache at #{primary_cache_path}. Switching cache to #{secondary_cache_path}") secondary_cache_path else primary_cache_path end end end # Returns true only if the path exists and is readable and writeable for the user. def self.path_accessible?(path) File.exists?(path) && File.readable?(path) && File.writable?(path) end # Where cookbook files are stored on the server (by content checksum) default(:checksum_path) { PathHelper.join(cache_path, "checksums") } # Where chef's cache files should be stored default(:file_cache_path) { PathHelper.join(cache_path, "cache") } # Where backups of chef-managed files should go default(:file_backup_path) { PathHelper.join(cache_path, "backup") } # The chef-client (or solo) lockfile. # # If your `file_cache_path` resides on a NFS (or non-flock()-supporting # fs), it's recommended to set this to something like # '/tmp/chef-client-running.pid' default(:lockfile) { PathHelper.join(file_cache_path, "chef-client-running.pid") } ## Daemonization Settings ## # What user should Chef run as? default :user, nil default :group, nil default :umask, 0022 # Valid log_levels are: # * :debug # * :info # * :warn # * :fatal # These work as you'd expect. There is also a special `:auto` setting. # When set to :auto, Chef will auto adjust the log verbosity based on # context. When a tty is available (usually because the user is running chef # in a console), the log level is set to :warn, and output formatters are # used as the primary mode of output. When a tty is not available, the # logger is the primary mode of output, and the log level is set to :info default :log_level, :auto # Logging location as either an IO stream or string representing log file path default :log_location, STDOUT # Using `force_formatter` causes chef to default to formatter output when STDOUT is not a tty default :force_formatter, false # Using `force_logger` causes chef to default to logger output when STDOUT is a tty default :force_logger, false default :http_retry_count, 5 default :http_retry_delay, 5 default :interval, nil default :once, nil default :json_attribs, nil # toggle info level log items that can create a lot of output default :verbose_logging, true default :node_name, nil default :diff_disabled, false default :diff_filesize_threshold, 10000000 default :diff_output_threshold, 1000000 default :local_mode, false default :pid_file, nil # Whether Chef Zero local mode should bind to a port. All internal requests # will go through the socketless code path regardless, so the socket is # only needed if other processes will connect to the local mode server. # # For compatibility this is set to true but it will be changed to false in # the future. default :listen, true config_context :chef_zero do config_strict_mode true default(:enabled) { Chef::Config.local_mode } default :host, 'localhost' default :port, 8889.upto(9999) # Will try ports from 8889-9999 until one works end default :chef_server_url, "https://localhost:443" default :rest_timeout, 300 default :yum_timeout, 900 default :yum_lock_timeout, 30 default :solo, false default :splay, nil default :why_run, false default :color, false default :client_fork, true default :ez, false default :enable_reporting, true default :enable_reporting_url_fatals, false # Possible values for :audit_mode # :enabled, :disabled, :audit_only, # # TODO: 11 Dec 2014: Currently audit-mode is an experimental feature # and is disabled by default. When users choose to enable audit-mode, # a warning is issued in application/client#reconfigure. # This can be removed when audit-mode is enabled by default. default :audit_mode, :disabled # Chef only needs ohai to run the hostname plugin for the most basic # functionality. If the rest of the ohai plugins are not needed (like in # most of our testing scenarios) default :minimal_ohai, false # Policyfile is an experimental feature where a node gets its run list and # cookbook version set from a single document on the server instead of # expanding the run list and having the server compute the cookbook version # set based on environment constraints. # # Because this feature is experimental, it is not recommended for # production use. Developent/release of this feature may not adhere to # semver guidelines. default :use_policyfile, false # Set these to enable SSL authentication / mutual-authentication # with the server # Client side SSL cert/key for mutual auth default :ssl_client_cert, nil default :ssl_client_key, nil # Whether or not to verify the SSL cert for all HTTPS requests. When set to # :verify_peer (default), all HTTPS requests will be validated regardless of other # SSL verification settings. When set to :verify_none no HTTPS requests will # be validated. default :ssl_verify_mode, :verify_peer # Whether or not to verify the SSL cert for HTTPS requests to the Chef # server API. If set to `true`, the server's cert will be validated # regardless of the :ssl_verify_mode setting. This is set to `true` when # running in local-mode. # NOTE: This is a workaround until verify_peer is enabled by default. default(:verify_api_cert) { Chef::Config.local_mode } # Path to the default CA bundle files. default :ssl_ca_path, nil default(:ssl_ca_file) do if Chef::Platform.windows? and embedded_path = embedded_dir cacert_path = File.join(embedded_path, "ssl/certs/cacert.pem") cacert_path if File.exist?(cacert_path) else nil end end # A directory that contains additional SSL certificates to trust. Any # certificates in this directory will be added to whatever CA bundle ruby # is using. Use this to add self-signed certs for your Chef Server or local # HTTP file servers. default(:trusted_certs_dir) { PathHelper.join(config_dir, "trusted_certs") } # Where should chef-solo download recipes from? default :recipe_url, nil # Sets the version of the signed header authentication protocol to use (see # the 'mixlib-authorization' project for more detail). Currently, versions # 1.0 and 1.1 are available; however, the chef-server must first be # upgraded to support version 1.1 before clients can begin using it. # # Version 1.1 of the protocol is required when using a `node_name` greater # than ~90 bytes (~90 ascii characters), so chef-client will automatically # switch to using version 1.1 when `node_name` is too large for the 1.0 # protocol. If you intend to use large node names, ensure that your server # supports version 1.1. Automatic detection of large node names means that # users will generally not need to manually configure this. # # In the future, this configuration option may be replaced with an # automatic negotiation scheme. default :authentication_protocol_version, "1.0" # This key will be used to sign requests to the Chef server. This location # must be writable by Chef during initial setup when generating a client # identity on the server. # # The chef-server will look up the public key for the client using the # `node_name` of the client. # # If chef-zero is enabled, this defaults to nil (no authentication). default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") } # When registering the client, should we allow the client key location to # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem # If the path of the key goes through a directory like /tmp this should # never be set to true or its possibly an easily exploitable security hole. default :follow_client_key_symlink, false # This secret is used to decrypt encrypted data bag items. default(:encrypted_data_bag_secret) do if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret")) platform_specific_path("/etc/chef/encrypted_data_bag_secret") else nil end end # As of Chef 11.0, version "1" is the default encrypted data bag item # format. Version "2" is available which adds encrypt-then-mac protection. # To maintain compatibility, versions other than 1 must be opt-in. # # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure. # Set this to `3` if you have chef-client 11.?.0+, ruby 2 and OpenSSL >= 1.0.1 in your infrastructure. (TODO) default :data_bag_encrypt_version, 1 # When reading data bag items, any supported version is accepted. However, # if all encrypted data bags have been generated with the version 2 format, # it is recommended to disable support for earlier formats to improve # security. For example, the version 2 format is identical to version 1 # except for the addition of an HMAC, so an attacker with MITM capability # could downgrade an encrypted data bag to version 1 as part of an attack. default :data_bag_decrypt_minimum_version, 0 # If there is no file in the location given by `client_key`, chef-client # will temporarily use the "validator" identity to generate one. If the # `client_key` is not present and the `validation_key` is also not present, # chef-client will not be able to authenticate to the server. # # The `validation_key` is never used if the `client_key` exists. # # If chef-zero is enabled, this defaults to nil (no authentication). default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") } default :validation_client_name, "chef-validator" # When creating a new client via the validation_client account, Chef 11 # servers allow the client to generate a key pair locally and send the # public key to the server. This is more secure and helps offload work from # the server, enhancing scalability. If enabled and the remote server # implements only the Chef 10 API, client registration will not work # properly. # # The default value is `true`. Set to `false` to disable client-side key # generation (server generates client keys). default(:local_key_generation) { true } # Zypper package provider gpg checks. Set to true to enable package # gpg signature checking. This will be default in the # future. Setting to false disables the warnings. # Leaving this set to nil or false is a security hazard! default :zypper_check_gpg, nil # Report Handlers default :report_handlers, [] # Event Handlers default :event_handlers, [] default :disable_event_loggers, false default :event_loggers do evt_loggers = [] if Chef::Platform::windows? and not Chef::Platform::windows_server_2003? evt_loggers << :win_evt end evt_loggers end # Exception Handlers default :exception_handlers, [] # Start handlers default :start_handlers, [] # Syntax Check Cache. Knife keeps track of files that is has already syntax # checked by storing files in this directory. `syntax_check_cache_path` is # the new (and preferred) configuration setting. If not set, knife will # fall back to using cache_options[:path], which is deprecated but exists in # many client configs generated by pre-Chef-11 bootstrappers. default(:syntax_check_cache_path) { cache_options[:path] } # Deprecated: # Move this to the default value of syntax_cache_path when this is removed. default(:cache_options) { { :path => PathHelper.join(config_dir, "syntaxcache") } } # Whether errors should be raised for deprecation warnings. When set to # `false` (the default setting), a warning is emitted but code using # deprecated methods/features/etc. should work normally otherwise. When set # to `true`, usage of deprecated methods/features will raise a # `DeprecatedFeatureError`. This is used by Chef's tests to ensure that # deprecated functionality is not used internally by Chef. End users # should generally leave this at the default setting (especially in # production), but it may be useful when testing cookbooks or other code if # the user wishes to aggressively address deprecations. default(:treat_deprecation_warnings_as_errors) do # Using an environment variable allows this setting to be inherited in # tests that spawn new processes. ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS") end # knife configuration data config_context :knife do default :ssh_port, nil default :ssh_user, nil default :ssh_attribute, nil default :ssh_gateway, nil default :bootstrap_version, nil default :bootstrap_proxy, nil default :bootstrap_template, nil default :secret, nil default :secret_file, nil default :identity_file, nil default :host_key_verify, nil default :forward_agent, nil default :sort_status_reverse, nil default :hints, {} end def self.set_defaults_for_windows # Those lists of regular expressions define what chef considers a # valid user and group name # From http://technet.microsoft.com/en-us/library/cc776019(WS.10).aspx principal_valid_regex_part = '[^"\/\\\\\[\]\:;|=,+*?<>]+' default :user_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ] default :group_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ] default :fatal_windows_admin_check, false end def self.set_defaults_for_nix # Those lists of regular expressions define what chef considers a # valid user and group name # # user/group cannot start with '-', '+' or '~' # user/group cannot contain ':', ',' or non-space-whitespace or null byte # everything else is allowed (UTF-8, spaces, etc) and we delegate to your O/S useradd program to barf or not # copies: http://anonscm.debian.org/viewvc/pkg-shadow/debian/trunk/debian/patches/506_relaxed_usernames?view=markup default :user_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ] default :group_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ] end # Those lists of regular expressions define what chef considers a # valid user and group name if Chef::Platform.windows? set_defaults_for_windows else set_defaults_for_nix end # This provides a hook which rspec can stub so that we can avoid twiddling # global state in tests. def self.env ENV end def self.windows_home_path Chef::Log.deprecation("Chef::Config.windows_home_path is now deprecated. Consider using Chef::Util::PathHelper.home instead.") PathHelper.home end # returns a platform specific path to the user home dir if set, otherwise default to current directory. default( :user_home ) { PathHelper.home || Dir.pwd } # Enable file permission fixup for selinux. Fixup will be done # only if selinux is enabled in the system. default :enable_selinux_file_permission_fixup, true # Use atomic updates (i.e. move operation) while updating contents # of the files resources. When set to false copy operation is # used to update files. default :file_atomic_update, true # There are 3 possible values for this configuration setting. # true => file staging is done in the destination directory # false => file staging is done via tempfiles under ENV['TMP'] # :auto => file staging will try using destination directory if possible and # will fall back to ENV['TMP'] if destination directory is not usable. default :file_staging_uses_destdir, :auto # Exit if another run is in progress and the chef-client is unable to # get the lock before time expires. If nil, no timeout is enforced. (Exits # immediately if 0.) default :run_lock_timeout, nil # Number of worker threads for syncing cookbooks in parallel. Increasing # this number can result in gateway errors from the server (namely 503 and 504). # If you are seeing this behavior while using the default setting, reducing # the number of threads will help. default :cookbook_sync_threads, 10 # At the beginning of the Chef Client run, the cookbook manifests are downloaded which # contain URLs for every file in every relevant cookbook. Most of the files # (recipes, resources, providers, libraries, etc) are immediately synchronized # at the start of the run. The handling of "files" and "templates" directories, # however, have two modes of operation. They can either all be downloaded immediately # at the start of the run (no_lazy_load==true) or else they can be lazily loaded as # cookbook_file or template resources are converged which require them (no_lazy_load==false). # # The advantage of lazily loading these files is that unnecessary files are not # synchronized. This may be useful to users with large files checked into cookbooks which # are only selectively downloaded to a subset of clients which use the cookbook. However, # better solutions are to either isolate large files into individual cookbooks and only # include those cookbooks in the run lists of the servers that need them -- or move to # using remote_file and a more appropriate backing store like S3 for large file # distribution. # # The disadvantages of lazily loading files are that users some time find it # confusing that their cookbooks are not fully synchronzied to the cache initially, # and more importantly the time-sensitive URLs which are in the manifest may time # out on long Chef runs before the resource that uses the file is converged # (leading to many confusing 403 errors on template/cookbook_file resources). # default :no_lazy_load, true # Default for the chef_gem compile_time attribute. Nil is the same as true but will emit # warnings on every use of chef_gem prompting the user to be explicit. If the user sets this to # true then the user will get backcompat behavior but with a single nag warning that cookbooks # may break with this setting in the future. The false setting is the recommended setting and # will become the default. default :chef_gem_compile_time, nil # A whitelisted array of attributes you want sent over the wire when node # data is saved. # The default setting is nil, which collects all data. Setting to [] will not # collect any data for save. default :automatic_attribute_whitelist, nil default :default_attribute_whitelist, nil default :normal_attribute_whitelist, nil default :override_attribute_whitelist, nil config_context :windows_service do # Set `watchdog_timeout` to the number of seconds to wait for a chef-client run # to finish default :watchdog_timeout, 2 * (60 * 60) # 2 hours end # Chef requires an English-language UTF-8 locale to function properly. We attempt # to use the 'locale -a' command and search through a list of preferences until we # find one that we can use. On Ubuntu systems we should find 'C.UTF-8' and be # able to use that even if there is no English locale on the server, but Mac, Solaris, # AIX, etc do not have that locale. We then try to find an English locale and fall # back to 'C' if we do not. The choice of fallback is pick-your-poison. If we try # to do the work to return a non-US UTF-8 locale then we fail inside of providers when # things like 'svn info' return Japanese and we can't parse them. OTOH, if we pick 'C' then # we will blow up on UTF-8 characters. Between the warn we throw and the Encoding # exception that ruby will throw it is more obvious what is broken if we drop UTF-8 by # default rather than drop English. # # If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly # available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'. def self.guess_internal_locale # https://github.com/opscode/chef/issues/2181 # Some systems have the `locale -a` command, but the result has # invalid characters for the default encoding. # # For example, on CentOS 6 with ENV['LANG'] = "en_US.UTF-8", # `locale -a`.split fails with ArgumentError invalid UTF-8 encoding. locales = shell_out_with_systems_locale!("locale -a").stdout.split case when locales.include?('C.UTF-8') 'C.UTF-8' when locales.include?('en_US.UTF-8'), locales.include?('en_US.utf8') 'en_US.UTF-8' when locales.include?('en.UTF-8') 'en.UTF-8' else # Will match en_ZZ.UTF-8, en_ZZ.utf-8, en_ZZ.UTF8, en_ZZ.utf8 guesses = locales.select { |l| l =~ /^en_.*UTF-?8$/i } unless guesses.empty? guessed_locale = guesses.first # Transform into the form en_ZZ.UTF-8 guessed_locale.gsub(/UTF-?8$/i, "UTF-8") else Chef::Log.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support." 'C' end end rescue if Chef::Platform.windows? Chef::Log.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else." else Chef::Log.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed." end 'en_US.UTF-8' end default :internal_locale, guess_internal_locale # Force UTF-8 Encoding, for when we fire up in the 'C' locale or other strange locales (e.g. # japanese windows encodings). If we do not do this, then knife upload will fail when a cookbook's # README.md has UTF-8 characters that do not encode in whatever surrounding encoding we have been # passed. Effectively, the Chef Ecosystem is globally UTF-8 by default. Anyone who wants to be # able to upload Shift_JIS or ISO-8859-1 files needs to mark *those* files explicitly with # magic tags to make ruby correctly identify the encoding being used. Changing this default will # break Chef community cookbooks and is very highly discouraged. default :ruby_encoding, Encoding::UTF_8 # If installed via an omnibus installer, this gives the path to the # "embedded" directory which contains all of the software packaged with # omnibus. This is used to locate the cacert.pem file on windows. def self.embedded_dir Pathname.new(_this_file).ascend do |path| if path.basename.to_s == "embedded" return path.to_s end end nil end # Path to this file in the current install. def self._this_file File.expand_path(__FILE__) end end end chef-12.3.0/lib/chef/knife/0000755000004100000410000000000012520074675015311 5ustar www-datawww-datachef-12.3.0/lib/chef/knife/diff.rb0000644000004100000410000000455412520074675016556 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Diff < Chef::ChefFS::Knife banner "knife diff PATTERNS" category "path-based" deps do require 'chef/chef_fs/command_line' end option :recurse, :long => '--[no-]recurse', :boolean => true, :default => true, :description => "List directories recursively." option :name_only, :long => '--name-only', :boolean => true, :description => "Only show names of modified files." option :name_status, :long => '--name-status', :boolean => true, :description => "Only show names and statuses of modified files: Added, Deleted, Modified, and Type Changed." option :diff_filter, :long => '--diff-filter=[(A|D|M|T)...[*]]', :description => "Select only files that are Added (A), Deleted (D), Modified (M), or have their type (i.e. regular file, directory) changed (T). Any combination of the filter characters (including none) can be used. When * (All-or-none) is added to the combination, all paths are selected if there is any file that matches other criteria in the comparison; if there is no file that matches other criteria, nothing is selected." option :cookbook_version, :long => '--cookbook-version VERSION', :description => 'Version of cookbook to download (if there are multiple versions and cookbook_versions is false)' def run if config[:name_only] output_mode = :name_only end if config[:name_status] output_mode = :name_status end patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ]) # Get the matches (recursively) error = false begin patterns.each do |pattern| found_error = Chef::ChefFS::CommandLine.diff_print(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, output_mode, proc { |entry| format_path(entry) }, config[:diff_filter], ui ) do |diff| stdout.print diff end error = true if found_error end rescue Chef::ChefFS::FileSystem::OperationFailedError => e ui.error "Failed on #{format_path(e.entry)} in #{e.operation}: #{e.message}" error = true end if error exit 1 end end end end end chef-12.3.0/lib/chef/knife/deps.rb0000644000004100000410000001121212520074675016566 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Deps < Chef::ChefFS::Knife banner "knife deps PATTERN1 [PATTERNn]" category "path-based" deps do require 'chef/chef_fs/file_system' require 'chef/run_list' end option :recurse, :long => '--[no-]recurse', :boolean => true, :description => "List dependencies recursively (default: true). Only works with --tree." option :tree, :long => '--tree', :boolean => true, :description => "Show dependencies in a visual tree. May show duplicates." option :remote, :long => '--remote', :boolean => true, :description => "List dependencies on the server instead of the local filesystem" attr_accessor :exit_code def run if config[:recurse] == false && !config[:tree] ui.error "--no-recurse requires --tree" exit(1) end config[:recurse] = true if config[:recurse].nil? @root = config[:remote] ? chef_fs : local_fs dependencies = {} pattern_args.each do |pattern| Chef::ChefFS::FileSystem.list(@root, pattern).each do |entry| if config[:tree] print_dependencies_tree(entry, dependencies) else print_flattened_dependencies(entry, dependencies) end end end exit exit_code if exit_code end def print_flattened_dependencies(entry, dependencies) if !dependencies[entry.path] dependencies[entry.path] = get_dependencies(entry) dependencies[entry.path].each do |child| child_entry = Chef::ChefFS::FileSystem.resolve_path(@root, child) print_flattened_dependencies(child_entry, dependencies) end output format_path(entry) end end def print_dependencies_tree(entry, dependencies, printed = {}, depth = 0) dependencies[entry.path] = get_dependencies(entry) if !dependencies[entry.path] output "#{' '*depth}#{format_path(entry)}" if !printed[entry.path] && (config[:recurse] || depth == 0) printed[entry.path] = true dependencies[entry.path].each do |child| child_entry = Chef::ChefFS::FileSystem.resolve_path(@root, child) print_dependencies_tree(child_entry, dependencies, printed, depth+1) end end end def get_dependencies(entry) begin if entry.parent && entry.parent.path == '/cookbooks' return entry.chef_object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}" } elsif entry.parent && entry.parent.path == '/nodes' node = Chef::JSONCompat.parse(entry.read) result = [] if node['chef_environment'] && node['chef_environment'] != '_default' result << "/environments/#{node['chef_environment']}.json" end if node['run_list'] result += dependencies_from_runlist(node['run_list']) end result elsif entry.parent && entry.parent.path == '/roles' role = Chef::JSONCompat.parse(entry.read) result = [] if role['run_list'] dependencies_from_runlist(role['run_list']).each do |dependency| result << dependency if !result.include?(dependency) end end if role['env_run_lists'] role['env_run_lists'].each_pair do |env,run_list| dependencies_from_runlist(run_list).each do |dependency| result << dependency if !result.include?(dependency) end end end result elsif !entry.exists? raise Chef::ChefFS::FileSystem::NotFoundError.new(entry) else [] end rescue Chef::ChefFS::FileSystem::NotFoundError => e ui.error "#{format_path(e.entry)}: No such file or directory" self.exit_code = 2 [] end end def dependencies_from_runlist(run_list) chef_run_list = Chef::RunList.new chef_run_list.reset!(run_list) chef_run_list.map do |run_list_item| case run_list_item.type when :role "/roles/#{run_list_item.name}.json" when :recipe if run_list_item.name =~ /(.+)::[^:]*/ "/cookbooks/#{$1}" else "/cookbooks/#{run_list_item.name}" end else raise "Unknown run list item type #{run_list_item.type}" end end end end end end chef-12.3.0/lib/chef/knife/client_edit.rb0000644000004100000410000000221712520074675020123 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientEdit < Knife deps do require 'chef/api_client' require 'chef/json_compat' end banner "knife client edit CLIENT (options)" def run @client_name = @name_args[0] if @client_name.nil? show_usage ui.fatal("You must specify a client name") exit 1 end edit_object(Chef::ApiClient, @client_name) end end end end chef-12.3.0/lib/chef/knife/show.rb0000644000004100000410000000312512520074675016617 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Show < Chef::ChefFS::Knife banner "knife show [PATTERN1 ... PATTERNn]" category "path-based" deps do require 'chef/chef_fs/file_system' require 'chef/chef_fs/file_system/not_found_error' end option :local, :long => '--local', :boolean => true, :description => "Show local files instead of remote" def run # Get the matches (recursively) error = false entry_values = parallelize(pattern_args) do |pattern| parallelize(Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern)) do |entry| if entry.dir? ui.error "#{format_path(entry)}: is a directory" if pattern.exact_path error = true nil else begin [entry, entry.read] rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e ui.error "#{format_path(e.entry)}: #{e.reason}." error = true nil rescue Chef::ChefFS::FileSystem::NotFoundError => e ui.error "#{format_path(e.entry)}: No such file or directory" error = true nil end end end end.flatten(1) entry_values.each do |entry, value| if entry output "#{format_path(entry)}:" output(format_for_display(value)) end end if error exit 1 end end end end end chef-12.3.0/lib/chef/knife/cookbook_site_vendor.rb0000644000004100000410000000227612520074675022054 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/knife' require 'chef/knife/cookbook_site_install' class Chef::Knife::CookbookSiteVendor < Chef::Knife::CookbookSiteInstall def self.load_deps superclass.load_deps end def self.options=(new_opts) superclass.options = new_opts end def self.options superclass.options end banner(<<-B) ************************************************* DEPRECATED: please use knife cookbook site install ************************************************* #{superclass.banner} B category 'deprecated' end chef-12.3.0/lib/chef/knife/role_env_run_list_clear.rb0000644000004100000410000000302312520074675022532 0ustar www-datawww-data# # Author:: Mike Fiedler () # Author:: William Albenzi () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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 RoleEnvRunListClear < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role env_run_list clear [ROLE] [ENVIRONMENT]" def clear_env_run_list(role, environment) nlist = [] role.env_run_lists_add(environment => nlist) end def run if @name_args.size > 2 ui.fatal "You must not supply an environment run list." show_usage exit 1 end role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = @name_args[1] clear_env_run_list(role, environment) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/ssh.rb0000644000004100000410000004711712520074675016445 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 Ssh < Knife deps do require 'net/ssh' require 'net/ssh/multi' require 'chef/monkey_patches/net-ssh-multi' require 'readline' require 'chef/exceptions' require 'chef/search/query' require 'chef/mixin/shell_out' require 'chef/mixin/command' require 'chef/util/path_helper' require 'mixlib/shellout' end include Chef::Mixin::ShellOut attr_writer :password banner "knife ssh QUERY COMMAND (options)" option :concurrency, :short => "-C NUM", :long => "--concurrency NUM", :description => "The number of concurrent connections", :default => nil, :proc => lambda { |o| o.to_i } option :attribute, :short => "-a ATTR", :long => "--attribute ATTR", :description => "The attribute to use for opening the connection - default depends on the context", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_attribute] = key.strip } option :manual, :short => "-m", :long => "--manual-list", :boolean => true, :description => "QUERY is a space separated list of servers", :default => false option :ssh_user, :short => "-x USERNAME", :long => "--ssh-user USERNAME", :description => "The ssh username" option :ssh_password_ng, :short => "-P [PASSWORD]", :long => "--ssh-password [PASSWORD]", :description => "The ssh password - will prompt if flag is specified but no password is given", # default to a value that can not be a password (boolean) # so we can effectively test if this parameter was specified # without a vlaue :default => false option :ssh_port, :short => "-p PORT", :long => "--ssh-port PORT", :description => "The ssh port", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip } option :ssh_gateway, :short => "-G GATEWAY", :long => "--ssh-gateway GATEWAY", :description => "The ssh gateway", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key.strip } option :forward_agent, :short => "-A", :long => "--forward-agent", :description => "Enable SSH agent forwarding", :boolean => true option :identity_file, :short => "-i IDENTITY_FILE", :long => "--identity-file IDENTITY_FILE", :description => "The SSH identity file used for authentication" option :host_key_verify, :long => "--[no-]host-key-verify", :description => "Verify host key, enabled by default.", :boolean => true, :default => true option :on_error, :short => '-e', :long => '--exit-on-error', :description => "Immediately exit if an error is encountered", :boolean => true, :proc => Proc.new { :raise } def session config[:on_error] ||= :skip ssh_error_handler = Proc.new do |server| case config[:on_error] when :skip ui.warn "Failed to connect to #{server.host} -- #{$!.class.name}: #{$!.message}" $!.backtrace.each { |l| Chef::Log.debug(l) } when :raise #Net::SSH::Multi magic to force exception to be re-raised. throw :go, :raise end end @session ||= Net::SSH::Multi.start(:concurrent_connections => config[:concurrency], :on_error => ssh_error_handler) end def configure_gateway config[:ssh_gateway] ||= Chef::Config[:knife][:ssh_gateway] if config[:ssh_gateway] gw_host, gw_user = config[:ssh_gateway].split('@').reverse gw_host, gw_port = gw_host.split(':') gw_opts = gw_port ? { :port => gw_port } : {} session.via(gw_host, gw_user || config[:ssh_user], gw_opts) end rescue Net::SSH::AuthenticationFailed user = gw_user || config[:ssh_user] prompt = "Enter the password for #{user}@#{gw_host}: " gw_opts.merge!(:password => prompt_for_password(prompt)) session.via(gw_host, user, gw_opts) end def configure_session list = config[:manual] ? @name_args[0].split(" ") : search_nodes if list.length == 0 if @action_nodes.length == 0 ui.fatal("No nodes returned from search!") else ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " + "but does not have the required attribute to establish the connection. " + "Try setting another attribute to open the connection using --attribute.") end exit 10 end session_from_list(list) end def search_nodes list = Array.new query = Chef::Search::Query.new @action_nodes = query.search(:node, @name_args[0])[0] @action_nodes.each do |item| # we should skip the loop to next iteration if the item # returned by the search is nil next if item.nil? # if a command line attribute was not passed, and we have a # cloud public_hostname, use that. see #configure_attribute # for the source of config[:attribute] and # config[:attribute_from_cli] if config[:attribute_from_cli] Chef::Log.debug("Using node attribute '#{config[:attribute_from_cli]}' from the command line as the ssh target") host = extract_nested_value(item, config[:attribute_from_cli]) elsif item[:cloud] && item[:cloud][:public_hostname] && !item[:cloud][:public_hostname].empty? Chef::Log.debug("Using node attribute 'cloud[:public_hostname]' automatically as the ssh target") host = item[:cloud][:public_hostname] else # ssh attribute from a configuration file or the default will land here Chef::Log.debug("Using node attribute '#{config[:attribute]}' as the ssh target") host = extract_nested_value(item, config[:attribute]) end # next if we couldn't find the specified attribute in the # returned node object next if host.nil? ssh_port = item[:cloud].nil? ? nil : item[:cloud][:public_ssh_port] srv = [host, ssh_port] list.push(srv) end list end def session_from_list(list) list.each do |item| host, ssh_port = item Chef::Log.debug("Adding #{host}") session_opts = {} ssh_config = Net::SSH.configuration_for(host) # Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user] user = config[:ssh_user] || ssh_config[:user] hostspec = user ? "#{user}@#{host}" : host session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file] session_opts[:keys_only] = true if config[:identity_file] session_opts[:password] = config[:ssh_password] if config[:ssh_password] session_opts[:forward_agent] = config[:forward_agent] session_opts[:port] = config[:ssh_port] || ssh_port || # Use cloud port if available Chef::Config[:knife][:ssh_port] || ssh_config[:port] session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug if !config[:host_key_verify] session_opts[:paranoid] = false session_opts[:user_known_hosts_file] = "/dev/null" end session.use(hostspec, session_opts) @longest = host.length if host.length > @longest end session end def fixup_sudo(command) command.sub(/^sudo/, 'sudo -p \'knife sudo password: \'') end def print_data(host, data) @buffers ||= {} if leftover = @buffers[host] @buffers[host] = nil print_data(host, leftover + data) else if newline_index = data.index("\n") line = data.slice!(0...newline_index) data.slice!(0) print_line(host, line) print_data(host, data) else @buffers[host] = data end end end def print_line(host, data) padding = @longest - host.length str = ui.color(host, :cyan) + (" " * (padding + 1)) + data ui.msg(str) end def ssh_command(command, subsession=nil) exit_status = 0 subsession ||= session command = fixup_sudo(command) command.force_encoding('binary') if command.respond_to?(:force_encoding) subsession.open_channel do |ch| ch.request_pty ch.exec command do |ch, success| raise ArgumentError, "Cannot execute #{command}" unless success ch.on_data do |ichannel, data| print_data(ichannel[:host], data) if data =~ /^knife sudo password: / print_data(ichannel[:host], "\n") ichannel.send_data("#{get_password}\n") end end ch.on_request "exit-status" do |ichannel, data| exit_status = [exit_status, data.read_long].max end end end session.loop exit_status end def get_password @password ||= prompt_for_password end def prompt_for_password(prompt = "Enter your password: ") ui.ask(prompt) { |q| q.echo = false } end # Present the prompt and read a single line from the console. It also # detects ^D and returns "exit" in that case. Adds the input to the # history, unless the input is empty. Loops repeatedly until a non-empty # line is input. def read_line loop do command = reader.readline("#{ui.color('knife-ssh>', :bold)} ", true) if command.nil? command = "exit" puts(command) else command.strip! end unless command.empty? return command end end end def reader Readline end def interactive puts "Connected to #{ui.list(session.servers_for.collect { |s| ui.color(s.host, :cyan) }, :inline, " and ")}" puts puts "To run a command on a list of servers, do:" puts " on SERVER1 SERVER2 SERVER3; COMMAND" puts " Example: on latte foamy; echo foobar" puts puts "To exit interactive mode, use 'quit!'" puts loop do command = read_line case command when 'quit!' puts 'Bye!' break when /^on (.+?); (.+)$/ raw_list = $1.split(" ") server_list = Array.new session.servers.each do |session_server| server_list << session_server if raw_list.include?(session_server.host) end command = $2 ssh_command(command, session.on(*server_list)) else ssh_command(command) end end end def screen tf = Tempfile.new("knife-ssh-screen") Chef::Util::PathHelper.home('.screenrc') do |screenrc_path| if File.exist? screenrc_path tf.puts("source #{screenrc_path}") end end tf.puts("caption always '%-Lw%{= BW}%50>%n%f* %t%{-}%+Lw%<'") tf.puts("hardstatus alwayslastline 'knife ssh #{@name_args[0]}'") window = 0 session.servers_for.each do |server| tf.print("screen -t \"#{server.host}\" #{window} ssh ") tf.print("-i #{config[:identity_file]} ") if config[:identity_file] server.user ? tf.puts("#{server.user}@#{server.host}") : tf.puts(server.host) window += 1 end tf.close exec("screen -c #{tf.path}") end def tmux ssh_dest = lambda do |server| identity = "-i #{config[:identity_file]} " if config[:identity_file] prefix = server.user ? "#{server.user}@" : "" "'ssh #{identity}#{prefix}#{server.host}'" end new_window_cmds = lambda do if session.servers_for.size > 1 [""] + session.servers_for[1..-1].map do |server| "new-window -a -n '#{server.host}' #{ssh_dest.call(server)}" end else [] end.join(" \\; ") end tmux_name = "'knife ssh #{@name_args[0].gsub(/:/,'=')}'" begin server = session.servers_for.first cmd = ["tmux new-session -d -s #{tmux_name}", "-n '#{server.host}'", ssh_dest.call(server), new_window_cmds.call].join(" ") shell_out!(cmd) exec("tmux attach-session -t #{tmux_name}") rescue Chef::Exceptions::Exec end end def macterm begin require 'appscript' rescue LoadError STDERR.puts "you need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install" raise end Appscript.app("/Applications/Utilities/Terminal.app").windows.first.activate Appscript.app("System Events").application_processes["Terminal.app"].keystroke("n", :using=>:command_down) term = Appscript.app('Terminal') window = term.windows.first.get (session.servers_for.size - 1).times do |i| window.activate Appscript.app("System Events").application_processes["Terminal.app"].keystroke("t", :using=>:command_down) end session.servers_for.each_with_index do |server, tab_number| cmd = "unset PROMPT_COMMAND; echo -e \"\\033]0;#{server.host}\\007\"; ssh #{server.user ? "#{server.user}@#{server.host}" : server.host}" Appscript.app('Terminal').do_script(cmd, :in => window.tabs[tab_number + 1].get) end end def configure_attribute # Setting 'knife[:ssh_attribute] = "foo"' in knife.rb => Chef::Config[:knife][:ssh_attribute] == 'foo' # Running 'knife ssh -a foo' => both Chef::Config[:knife][:ssh_attribute] && config[:attribute] == foo # Thus we can differentiate between a config file value and a command line override at this point by checking config[:attribute] # We can tell here if fqdn was passed from the command line, rather than being the default, by checking config[:attribute] # However, after here, we cannot tell these things, so we must preserve config[:attribute] config[:attribute_from_cli] = config[:attribute] config[:attribute] = (config[:attribute_from_cli] || Chef::Config[:knife][:ssh_attribute] || "fqdn").strip end def cssh cssh_cmd = nil %w[csshX cssh].each do |cmd| begin # Unix and Mac only cssh_cmd = shell_out!("which #{cmd}").stdout.strip break rescue Mixlib::ShellOut::ShellCommandFailed end end raise Chef::Exceptions::Exec, "no command found for cssh" unless cssh_cmd # pass in the consolidated itentity file option to cssh(X) if config[:identity_file] cssh_cmd << " --ssh_args '-i #{File.expand_path(config[:identity_file])}'" end session.servers_for.each do |server| cssh_cmd << " #{server.user ? "#{server.user}@#{server.host}" : server.host}" end Chef::Log.debug("starting cssh session with command: #{cssh_cmd}") exec(cssh_cmd) end def get_stripped_unfrozen_value(value) return nil if value.nil? value.strip end def configure_user config[:ssh_user] = get_stripped_unfrozen_value(config[:ssh_user] || Chef::Config[:knife][:ssh_user]) end # This is a bit overly complicated because of the way we want knife ssh to work with -P causing a password prompt for # the user, but we have to be conscious that this code gets included in knife bootstrap and knife * server create as # well. We want to change the semantics so that the default is false and 'nil' means -P without an argument on the # command line. But the other utilities expect nil to be the default and we can't prompt in that case. So we effectively # use ssh_password_ng to determine if we're coming from knife ssh or from the other utilities. The other utilties can # also be patched to use ssh_password_ng easily as long they follow the convention that the default is false. def configure_password if config.has_key?(:ssh_password_ng) && config[:ssh_password_ng].nil? # If the parameter is called on the command line with no value # it will set :ssh_password_ng = nil # This is where we want to trigger a prompt for password config[:ssh_password] = get_password else # if ssh_password_ng is false then it has not been set at all, and we may be in knife ec2 and still # using an old config[:ssh_password]. this is backwards compatibility. all knife cloud plugins should # be updated to use ssh_password_ng with a default of false and ssh_password should be retired, (but # we'll still need to use the ssh_password out of knife.rb if we find that). ssh_password = config.has_key?(:ssh_password_ng) ? config[:ssh_password_ng] : config[:ssh_password] # Otherwise, the password has either been specified on the command line, # in knife.rb, or key based auth will be attempted config[:ssh_password] = get_stripped_unfrozen_value(ssh_password || Chef::Config[:knife][:ssh_password]) end end def configure_identity_file config[:identity_file] = get_stripped_unfrozen_value(config[:identity_file] || Chef::Config[:knife][:ssh_identity_file]) end def extract_nested_value(data_structure, path_spec) ui.presenter.extract_nested_value(data_structure, path_spec) end def run extend Chef::Mixin::Command @longest = 0 configure_attribute configure_user configure_password configure_identity_file configure_gateway configure_session exit_status = case @name_args[1] when "interactive" interactive when "screen" screen when "tmux" tmux when "macterm" macterm when "cssh" cssh when "csshx" Chef::Log.warn("knife ssh csshx will be deprecated in a future release") Chef::Log.warn("please use knife ssh cssh instead") cssh else ssh_command(@name_args[1..-1].join(" ")) end session.close if exit_status != 0 exit exit_status else exit_status end end private :search_nodes end end end chef-12.3.0/lib/chef/knife/role_show.rb0000644000004100000410000000243112520074675017637 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleShow < Knife include Knife::Core::MultiAttributeReturnOption deps do require 'chef/node' require 'chef/json_compat' end banner "knife role show ROLE (options)" def run @role_name = @name_args[0] if @role_name.nil? show_usage ui.fatal("You must specify a role name") exit 1 end role = Chef::Role.load(@role_name) output(format_for_display(config[:environment] ? role.environment(config[:environment]) : role)) end end end end chef-12.3.0/lib/chef/knife/user_reregister.rb0000644000004100000410000000302612520074675021050 0ustar www-datawww-data# # 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 '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 chef-12.3.0/lib/chef/knife/role_run_list_clear.rb0000644000004100000410000000277312520074675021675 0ustar www-datawww-data# # Author:: Mike Fiedler () # Author:: William Albenzi () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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 RoleRunListClear < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role run_list clear [ROLE]" def clear_env_run_list(role, environment) nlist = [] role.env_run_lists_add(environment => nlist) end def run if @name_args.size > 2 ui.fatal "You must not supply an environment run list." show_usage exit 1 end role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = "_default" clear_env_run_list(role, environment) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/cookbook_upload.rb0000644000004100000410000002767112520074675021025 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Nuo Yan () # Copyright:: Copyright (c) 2009, 2010 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' require 'chef/cookbook_uploader' class Chef class Knife class CookbookUpload < Knife CHECKSUM = "checksum" MATCH_CHECKSUM = /[0-9a-f]{32,}/ deps do require 'chef/exceptions' require 'chef/cookbook_loader' require 'chef/cookbook_uploader' end banner "knife cookbook upload [COOKBOOKS...] (options)" option :cookbook_path, :short => "-o PATH:PATH", :long => "--cookbook-path PATH:PATH", :description => "A colon-separated path to look for cookbooks in", :proc => lambda { |o| o.split(":") } option :freeze, :long => '--freeze', :description => 'Freeze this version of the cookbook so that it cannot be overwritten', :boolean => true option :all, :short => "-a", :long => "--all", :description => "Upload all cookbooks, rather than just a single cookbook" option :force, :long => '--force', :boolean => true, :description => "Update cookbook versions even if they have been frozen" option :concurrency, :long => '--concurrency NUMBER_OF_THREADS', :description => "How many concurrent threads will be used", :default => 10, :proc => lambda { |o| o.to_i } option :environment, :short => '-E', :long => '--environment ENVIRONMENT', :description => "Set ENVIRONMENT's version dependency match the version you're uploading.", :default => nil option :depends, :short => "-d", :long => "--include-dependencies", :description => "Also upload cookbook dependencies" def run # Sanity check before we load anything from the server unless config[:all] if @name_args.empty? show_usage ui.fatal("You must specify the --all flag or at least one cookbook name") exit 1 end end config[:cookbook_path] ||= Chef::Config[:cookbook_path] if @name_args.empty? and ! config[:all] show_usage ui.fatal("You must specify the --all flag or at least one cookbook name") exit 1 end assert_environment_valid! warn_about_cookbook_shadowing version_constraints_to_update = {} upload_failures = 0 upload_ok = 0 # Get a list of cookbooks and their versions from the server # to check for the existence of a cookbook's dependencies. @server_side_cookbooks = Chef::CookbookVersion.list_all_versions justify_width = @server_side_cookbooks.map {|name| name.size}.max.to_i + 2 if config[:all] cookbook_repo.load_cookbooks cookbooks_for_upload = [] cookbook_repo.each do |cookbook_name, cookbook| cookbooks_for_upload << cookbook cookbook.freeze_version if config[:freeze] version_constraints_to_update[cookbook_name] = cookbook.version end if cookbooks_for_upload.any? begin upload(cookbooks_for_upload, justify_width) rescue Exceptions::CookbookFrozen ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.") end ui.info("Uploaded all cookbooks.") else cookbook_path = config[:cookbook_path].respond_to?(:join) ? config[:cookbook_path].join(', ') : config[:cookbook_path] ui.warn("Could not find any cookbooks in your cookbook path: #{cookbook_path}. Use --cookbook-path to specify the desired path.") end else if @name_args.empty? show_usage ui.error("You must specify the --all flag or at least one cookbook name") exit 1 end cookbooks_to_upload.each do |cookbook_name, cookbook| cookbook.freeze_version if config[:freeze] begin upload([cookbook], justify_width) upload_ok += 1 version_constraints_to_update[cookbook_name] = cookbook.version rescue Exceptions::CookbookNotFoundInRepo => e upload_failures += 1 ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it") Log.debug(e) upload_failures += 1 rescue Exceptions::CookbookFrozen ui.warn("Not updating version constraints for #{cookbook_name} in the environment as the cookbook is frozen.") upload_failures += 1 end end upload_failures += @name_args.length - @cookbooks_to_upload.length if upload_failures == 0 ui.info "Uploaded #{upload_ok} cookbook#{upload_ok > 1 ? "s" : ""}." elsif upload_failures > 0 && upload_ok > 0 ui.warn "Uploaded #{upload_ok} cookbook#{upload_ok > 1 ? "s" : ""} ok but #{upload_failures} " + "cookbook#{upload_failures > 1 ? "s" : ""} upload failed." elsif upload_failures > 0 && upload_ok == 0 ui.error "Failed to upload #{upload_failures} cookbook#{upload_failures > 1 ? "s" : ""}." exit 1 end end unless version_constraints_to_update.empty? update_version_constraints(version_constraints_to_update) if config[:environment] end end def cookbooks_to_upload @cookbooks_to_upload ||= if config[:all] cookbook_repo.load_cookbooks else upload_set = {} @name_args.each do |cookbook_name| begin if ! upload_set.has_key?(cookbook_name) upload_set[cookbook_name] = cookbook_repo[cookbook_name] if config[:depends] upload_set[cookbook_name].metadata.dependencies.each { |dep, ver| @name_args << dep } end end rescue Exceptions::CookbookNotFoundInRepo => e ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it") Log.debug(e) end end upload_set end end def cookbook_repo @cookbook_loader ||= begin Chef::Cookbook::FileVendor.fetch_from_disk(config[:cookbook_path]) Chef::CookbookLoader.new(config[:cookbook_path]) end end def update_version_constraints(new_version_constraints) new_version_constraints.each do |cookbook_name, version| environment.cookbook_versions[cookbook_name] = "= #{version}" end environment.save end def environment @environment ||= config[:environment] ? Environment.load(config[:environment]) : nil end def warn_about_cookbook_shadowing # because cookbooks are lazy-loaded, we have to force the loader # to load the cookbooks the user intends to upload here: cookbooks_to_upload unless cookbook_repo.merged_cookbooks.empty? ui.warn "* " * 40 ui.warn(<<-WARNING) The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path. A composite version of these cookbooks has been compiled for uploading. #{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer be able to have the same version of a cookbook in multiple places in your cookbook_path. WARNING ui.warn "The affected cookbooks are located:" ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths) ui.warn "* " * 40 end end private def assert_environment_valid! environment rescue Net::HTTPServerException => e if e.response.code.to_s == "404" ui.error "The environment #{config[:environment]} does not exist on the server, aborting." Log.debug(e) exit 1 else raise end end def upload(cookbooks, justify_width) cookbooks.each do |cb| ui.info("Uploading #{cb.name.to_s.ljust(justify_width + 10)} [#{cb.version}]") check_for_broken_links!(cb) check_for_dependencies!(cb) end Chef::CookbookUploader.new(cookbooks, :force => config[:force], :concurrency => config[:concurrency]).upload_cookbooks rescue Chef::Exceptions::CookbookFrozen => e ui.error e raise end def check_for_broken_links!(cookbook) # MUST!! dup the cookbook version object--it memoizes its # manifest object, but the manifest becomes invalid when you # regenerate the metadata broken_files = cookbook.dup.manifest_records_by_path.select do |path, info| info[CHECKSUM].nil? || info[CHECKSUM] !~ MATCH_CHECKSUM end unless broken_files.empty? broken_filenames = Array(broken_files).map {|path, info| path} ui.error "The cookbook #{cookbook.name} has one or more broken files" ui.error "This is probably caused by broken symlinks in the cookbook directory" ui.error "The broken file(s) are: #{broken_filenames.join(' ')}" exit 1 end end def check_for_dependencies!(cookbook) # for all dependencies, check if the version is on the server, or # the version is in the cookbooks being uploaded. If not, exit and warn the user. missing_dependencies = cookbook.metadata.dependencies.reject do |cookbook_name, version| check_server_side_cookbooks(cookbook_name, version) || check_uploading_cookbooks(cookbook_name, version) end unless missing_dependencies.empty? missing_cookbook_names = missing_dependencies.map { |cookbook_name, version| "'#{cookbook_name}' version '#{version}'"} ui.error "Cookbook #{cookbook.name} depends on cookbooks which are not currently" ui.error "being uploaded and cannot be found on the server." ui.error "The missing cookbook(s) are: #{missing_cookbook_names.join(', ')}" exit 1 end end def check_server_side_cookbooks(cookbook_name, version) if @server_side_cookbooks[cookbook_name].nil? false else versions = @server_side_cookbooks[cookbook_name]['versions'].collect {|versions| versions["version"]} Log.debug "Versions of cookbook '#{cookbook_name}' returned by the server: #{versions.join(", ")}" @server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash| if Chef::VersionConstraint.new(version).include?(versions_hash["version"]) Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to cookbook version '#{versions_hash['version']}' on the server" return true end end false end end def check_uploading_cookbooks(cookbook_name, version) if (! cookbooks_to_upload[cookbook_name].nil?) && Chef::VersionConstraint.new(version).include?(cookbooks_to_upload[cookbook_name].version) Log.debug "Matched cookbook '#{cookbook_name}' with constraint '#{version}' to a local cookbook." return true end false end end end end chef-12.3.0/lib/chef/knife/role_bulk_delete.rb0000644000004100000410000000344712520074675021146 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleBulkDelete < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role bulk delete REGEX (options)" def run if @name_args.length < 1 ui.error("You must supply a regular expression to match the results against") exit 1 end all_roles = Chef::Role.list(true) matcher = /#{@name_args[0]}/ roles_to_delete = {} all_roles.each do |name, role| next unless name =~ matcher roles_to_delete[role.name] = role end if roles_to_delete.empty? ui.info "No roles match the expression /#{@name_args[0]}/" exit 0 end ui.msg("The following roles will be deleted:") ui.msg("") ui.msg(ui.list(roles_to_delete.keys.sort, :columns_down)) ui.msg("") ui.confirm("Are you sure you want to delete these roles") roles_to_delete.sort.each do |name, role| role.destroy ui.msg("Deleted role #{name}") end end end end end chef-12.3.0/lib/chef/knife/cookbook_test.rb0000644000004100000410000000566512520074675020517 0ustar www-datawww-data# # # Author:: Adam Jacob () # Author:: Matthew Kent () # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2010 Matthew Kent # 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 CookbookTest < Knife deps do require 'chef/cookbook_loader' require 'chef/cookbook/syntax_check' end banner "knife cookbook test [COOKBOOKS...] (options)" option :cookbook_path, :short => "-o PATH:PATH", :long => "--cookbook-path PATH:PATH", :description => "A colon-separated path to look for cookbooks in", :proc => lambda { |o| o.split(":") } option :all, :short => "-a", :long => "--all", :description => "Test all cookbooks, rather than just a single cookbook" def run ui.warn("DEPRECATED: Please use ChefSpec or Rubocop to syntax-check cookbooks.") config[:cookbook_path] ||= Chef::Config[:cookbook_path] checked_a_cookbook = false if config[:all] cl = cookbook_loader cl.load_cookbooks cl.each do |key, cookbook| checked_a_cookbook = true test_cookbook(key) end else @name_args.each do |cb| ui.info "checking #{cb}" next unless cookbook_loader.cookbook_exists?(cb) checked_a_cookbook = true test_cookbook(cb) end end unless checked_a_cookbook ui.warn("No cookbooks to test in #{Array(config[:cookbook_path]).join(',')} - is your cookbook path misconfigured?") end end def test_cookbook(cookbook) ui.info("Running syntax check on #{cookbook}") Array(config[:cookbook_path]).reverse.each do |path| syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook, path) test_ruby(syntax_checker) test_templates(syntax_checker) end end def test_ruby(syntax_checker) ui.info("Validating ruby files") exit(1) unless syntax_checker.validate_ruby_files end def test_templates(syntax_checker) ui.info("Validating templates") exit(1) unless syntax_checker.validate_templates end def cookbook_loader @cookbook_loader ||= Chef::CookbookLoader.new(config[:cookbook_path]) end end end end chef-12.3.0/lib/chef/knife/node_from_file.rb0000644000004100000410000000246612520074675020615 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeFromFile < Knife deps do require 'chef/node' require 'chef/json_compat' require 'chef/knife/core/object_loader' end banner "knife node from file FILE (options)" def loader @loader ||= Knife::Core::ObjectLoader.new(Chef::Node, ui) end def run @name_args.each do |arg| updated = loader.load_from('nodes', arg) updated.save output(format_for_display(updated)) if config[:print_after] ui.info("Updated Node #{updated.name}!") end end end end end chef-12.3.0/lib/chef/knife/help_topics.rb0000644000004100000410000000112312520074675020144 0ustar www-datawww-data# Do not edit this file by hand # This file is autogenerated by the docs:list rake task from the available manpages HELP_TOPICS = ["chef-shell", "knife-bootstrap", "knife-client", "knife-configure", "knife-cookbook-site", "knife-cookbook", "knife-data-bag", "knife-delete", "knife-deps", "knife-diff", "knife-download", "knife-edit", "knife-environment", "knife-exec", "knife-index-rebuild", "knife-list", "knife-node", "knife-raw", "knife-recipe-list", "knife-role", "knife-search", "knife-show", "knife-ssh", "knife-status", "knife-tag", "knife-upload", "knife-user", "knife-xargs", "knife"] chef-12.3.0/lib/chef/knife/cookbook_site_unshare.rb0000644000004100000410000000317612520074675022224 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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 CookbookSiteUnshare < Knife deps do require 'chef/json_compat' end banner "knife cookbook site unshare COOKBOOK" category "cookbook site" def run @cookbook_name = @name_args[0] if @cookbook_name.nil? show_usage ui.fatal "You must provide the name of the cookbook to unshare" exit 1 end confirm "Do you really want to unshare the cookbook #{@cookbook_name}" begin rest.delete_rest "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}" rescue Net::HTTPServerException => e raise e unless e.message =~ /Forbidden/ ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it." exit 1 end ui.info "Unshared cookbook #{@cookbook_name}" end end end end chef-12.3.0/lib/chef/knife/role_delete.rb0000644000004100000410000000217612520074675020127 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleDelete < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role delete ROLE (options)" def run @role_name = @name_args[0] if @role_name.nil? show_usage ui.fatal("You must specify a role name") exit 1 end delete_object(Chef::Role, @role_name) end end end end chef-12.3.0/lib/chef/knife/core/0000755000004100000410000000000012520074675016241 5ustar www-datawww-datachef-12.3.0/lib/chef/knife/core/node_presenter.rb0000644000004100000410000001051412520074675021603 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/knife/core/text_formatter' require 'chef/knife/core/generic_presenter' class Chef class Knife module Core # This module may be included into a knife subcommand class to automatically # add configuration options used by the NodePresenter module NodeFormattingOptions # :nodoc: # Would prefer to do this in a rational way, but can't be done b/c of # Mixlib::CLI's design :( def self.included(includer) includer.class_eval do option :medium_output, :short => '-m', :long => '--medium', :boolean => true, :default => false, :description => 'Include normal attributes in the output' option :long_output, :short => '-l', :long => '--long', :boolean => true, :default => false, :description => 'Include all attributes in the output' end end end #==Chef::Knife::Core::NodePresenter # A customized presenter for Chef::Node objects. Supports variable-length # output formats for displaying node data class NodePresenter < GenericPresenter def format(data) if parse_format_option == :json summarize_json(data) else super end end def summarize_json(data) if data.kind_of?(Chef::Node) node = data result = {} result["name"] = node.name result["chef_environment"] = node.chef_environment result["run_list"] = node.run_list result["normal"] = node.normal_attrs if config[:long_output] result["default"] = node.default_attrs result["override"] = node.override_attrs result["automatic"] = node.automatic_attrs end Chef::JSONCompat.to_json_pretty(result) else Chef::JSONCompat.to_json_pretty(data) end end # Converts a Chef::Node object to a string suitable for output to a # terminal. If config[:medium_output] or config[:long_output] are set # the volume of output is adjusted accordingly. Uses colors if enabled # in the ui object. def summarize(data) if data.kind_of?(Chef::Node) node = data # special case ec2 with their split horizon whatsis. ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress] summarized=<<-SUMMARY #{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)} #{key('Environment:')} #{node.chef_environment} #{key('FQDN:')} #{node[:fqdn]} #{key('IP:')} #{ip} #{key('Run List:')} #{node.run_list} #{key('Roles:')} #{Array(node[:roles]).join(', ')} #{key('Recipes:')} #{Array(node[:recipes]).join(', ')} #{key('Platform:')} #{node[:platform]} #{node[:platform_version]} #{key('Tags:')} #{Array(node[:tags]).join(', ')} SUMMARY if config[:medium_output] || config[:long_output] summarized +=<<-MORE #{key('Attributes:')} #{text_format(node.normal_attrs)} MORE end if config[:long_output] summarized +=<<-MOST #{key('Default Attributes:')} #{text_format(node.default_attrs)} #{key('Override Attributes:')} #{text_format(node.override_attrs)} #{key('Automatic Attributes (Ohai Data):')} #{text_format(node.automatic_attrs)} MOST end summarized else super end end def key(key_text) ui.color(key_text, :cyan) end end end end end chef-12.3.0/lib/chef/knife/core/generic_presenter.rb0000644000004100000410000001703012520074675022272 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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 'chef/knife/core/text_formatter' class Chef class Knife module Core # Allows includer knife commands to return multiple attributes # @brief knife node show NAME -a ATTR1 -a ATTR2 module MultiAttributeReturnOption # :nodoc: def self.included(includer) includer.class_eval do @attrs_to_show = [] option :attribute, :short => "-a ATTR1 [-a ATTR2]", :long => "--attribute ATTR1 [--attribute ATTR2] ", :proc => lambda {|val| @attrs_to_show << val}, :description => "Show one or more attributes" end end end #==Chef::Knife::Core::GenericPresenter # The base presenter class for displaying structured data in knife commands. # This is not an abstract base class, and it is suitable for displaying # most kinds of objects that knife needs to display. class GenericPresenter attr_reader :ui attr_reader :config # Instaniates a new GenericPresenter. This is generally handled by the # Chef::Knife::UI object, though you need to match the signature of this # method if you intend to use your own presenter instead. def initialize(ui, config) @ui, @config = ui, config end # Is the selected output format a data interchange format? # Returns true if the selected output format is json or yaml, false # otherwise. Knife search uses this to adjust its data output so as not # to produce invalid JSON output. def interchange? case parse_format_option when :json, :yaml true else false end end # Returns a String representation of +data+ that is suitable for output # to a terminal or perhaps for data interchange with another program. # The representation of the +data+ depends on the value of the # `config[:format]` setting. def format(data) case parse_format_option when :summary summarize(data) when :text text_format(data) when :json Chef::JSONCompat.to_json_pretty(data) when :yaml require 'yaml' YAML::dump(data) when :pp require 'stringio' # If you were looking for some attribute and there is only one match # just dump the attribute value if config[:attribute] and data.length == 1 data.values[0] else out = StringIO.new PP.pp(data, out) out.string end end end # Converts the user-supplied value of `config[:format]` to a Symbol # representing the desired output format. # ===Returns # returns one of :summary, :text, :json, :yaml, or :pp # ===Raises # Raises an ArgumentError if the desired output format could not be # determined from the value of `config[:format]` def parse_format_option case config[:format] when "summary", /^s/, nil :summary when "text", /^t/ :text when "json", /^j/ :json when "yaml", /^y/ :yaml when "pp", /^p/ :pp else raise ArgumentError, "Unknown output format #{config[:format]}" end end # Summarize the data. Defaults to text format output, # which may not be very summary-like def summarize(data) text_format(data) end # Converts the +data+ to a String in the text format. Uses # Chef::Knife::Core::TextFormatter def text_format(data) TextFormatter.new(data, ui).formatted_data end def format_list_for_display(list) config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b } end def format_for_display(data) if formatting_subset_of_data? format_data_subset_for_display(data) elsif config[:id_only] name_or_id_for(data) elsif config[:environment] && data.respond_to?(:chef_environment) {"chef_environment" => data.chef_environment} else data end end def format_data_subset_for_display(data) subset = if config[:attribute] result = {} Array(config[:attribute]).each do |nested_value_spec| nested_value = extract_nested_value(data, nested_value_spec) result[nested_value_spec] = nested_value end result elsif config[:run_list] run_list = data.run_list.run_list { "run_list" => run_list } else raise ArgumentError, "format_data_subset_for_display requires attribute, run_list, or id_only config option to be set" end {name_or_id_for(data) => subset } end def name_or_id_for(data) data.respond_to?(:name) ? data.name : data["id"] end def formatting_subset_of_data? config[:attribute] || config[:run_list] end def extract_nested_value(data, nested_value_spec) nested_value_spec.split(".").each do |attr| if data.nil? nil # don't get no method error on nil # Must check :[] before attr because spec can include # `keys` - want the key named `keys`, not a list of # available keys. elsif data.respond_to?(:[]) data = data[attr] elsif data.respond_to?(attr.to_sym) data = data.send(attr.to_sym) else data = begin data.send(attr.to_sym) rescue NoMethodError nil end end end ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data end def format_cookbook_list_for_display(item) if config[:with_uri] item.inject({}) do |collected, (cookbook, versions)| collected[cookbook] = Hash.new versions['versions'].each do |ver| collected[cookbook][ver['version']] = ver['url'] end collected end else versions_by_cookbook = item.inject({}) do |collected, ( cookbook, versions )| collected[cookbook] = versions["versions"].map {|v| v['version']} collected end key_length = versions_by_cookbook.empty? ? 0 : versions_by_cookbook.keys.map {|name| name.size }.max + 2 versions_by_cookbook.sort.map do |cookbook, versions| "#{cookbook.ljust(key_length)} #{versions.join(' ')}" end end end end end end end chef-12.3.0/lib/chef/knife/core/bootstrap_context.rb0000644000004100000410000001460512520074675022355 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/run_list' require 'chef/util/path_helper' class Chef class Knife module Core # Instances of BootstrapContext are the context objects (i.e., +self+) for # bootstrap templates. For backwards compatibility, they +must+ set the # following instance variables: # * @config - a hash of knife's config values # * @run_list - the run list for the node to boostrap # class BootstrapContext attr_accessor :client_pem def initialize(config, run_list, chef_config, secret = nil) @config = config @run_list = run_list @chef_config = chef_config @secret = secret end def bootstrap_environment @chef_config[:environment] || '_default' end def validation_key if File.exist?(File.expand_path(@chef_config[:validation_key])) IO.read(File.expand_path(@chef_config[:validation_key])) else false end end def encrypted_data_bag_secret @secret end # Contains commands and content, see trusted_certs_content # TODO: Rename to trusted_certs_script def trusted_certs @trusted_certs ||= trusted_certs_content end def config_content client_rb = <<-CONFIG log_location STDOUT chef_server_url "#{@chef_config[:chef_server_url]}" validation_client_name "#{@chef_config[:validation_client_name]}" CONFIG if @config[:chef_node_name] client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n} else client_rb << "# Using default node name (fqdn)\n" end # We configure :verify_api_cert only when it's overridden on the CLI # or when specified in the knife config. if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert) value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert] client_rb << %Q{verify_api_cert #{value}\n} end # We configure :ssl_verify_mode only when it's overridden on the CLI # or when specified in the knife config. if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode) value = case @config[:node_ssl_verify_mode] when "peer" :verify_peer when "none" :verify_none when nil knife_config[:ssl_verify_mode] else nil end if value client_rb << %Q{ssl_verify_mode :#{value}\n} end end if @config[:ssl_verify_mode] client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n} end if knife_config[:bootstrap_proxy] client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n} client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n} end if knife_config[:bootstrap_no_proxy] client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} end if encrypted_data_bag_secret client_rb << %Q{encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"\n} end unless trusted_certs.empty? client_rb << %Q{trusted_certs_dir "/etc/chef/trusted_certs"\n} end client_rb end def start_chef # If the user doesn't have a client path configure, let bash use the PATH for what it was designed for client_path = @chef_config[:chef_client_path] || 'chef-client' s = "#{client_path} -j /etc/chef/first-boot.json" s << ' -l debug' if @config[:verbosity] and @config[:verbosity] >= 2 s << " -E #{bootstrap_environment}" s end def knife_config @chef_config.key?(:knife) ? @chef_config[:knife] : {} end # # chef version string to fetch the latest current version from omnitruck # If user is on X.Y.Z bootstrap will use the latest X release # X here can be 10 or 11 def latest_current_chef_version_string installer_version_string = nil if @config[:prerelease] installer_version_string = ["-p"] else chef_version_string = if knife_config[:bootstrap_version] knife_config[:bootstrap_version] else Chef::VERSION.split(".").first end installer_version_string = ["-v", chef_version_string] # If bootstrapping a pre-release version add -p to the installer string if chef_version_string.split(".").length > 3 installer_version_string << "-p" end end installer_version_string.join(" ") end def first_boot (@config[:first_boot_attributes] || {}).merge(:run_list => @run_list) end private # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped # This string should contain both the commands necessary to both create the files, as well as their content def trusted_certs_content content = "" if @chef_config[:trusted_certs_dir] Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert| content << "cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'\n" + IO.read(File.expand_path(cert)) + "\nEOP\n" end end content end end end end end chef-12.3.0/lib/chef/knife/core/object_loader.rb0000644000004100000410000000667612520074675021401 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'ffi_yajl' require 'chef/util/path_helper' class Chef class Knife module Core class ObjectLoader attr_reader :ui attr_reader :klass class ObjectType FILE = 1 FOLDER = 2 end def initialize(klass, ui) @klass = klass @ui = ui end def load_from(repo_location, *components) unless object_file = find_file(repo_location, *components) ui.error "Could not find or open file '#{components.last}' in current directory or in '#{repo_location}/#{components.join('/')}'" exit 1 end object_from_file(object_file) end # When someone makes this awesome, please update the above error message. def find_file(repo_location, *components) if file_exists_and_is_readable?(File.expand_path( components.last )) File.expand_path( components.last ) else relative_path = File.join(Dir.pwd, repo_location, *components) if file_exists_and_is_readable?(relative_path) relative_path else nil end end end # Find all objects in the given location # If the object type is File it will look for all *.{json,rb} # files, otherwise it will lookup for folders only (useful for # data_bags) # # @param [String] path - base look up location # # @return [Array] basenames of the found objects # # @api public def find_all_objects(path) path = File.join(Chef::Util::PathHelper.escape_glob(File.expand_path(path)), '*') path << '.{json,rb}' objects = Dir.glob(path) objects.map { |o| File.basename(o) } end def find_all_object_dirs(path) path = File.join(Chef::Util::PathHelper.escape_glob(File.expand_path(path)), '*') objects = Dir.glob(path) objects.delete_if { |o| !File.directory?(o) } objects.map { |o| File.basename(o) } end def object_from_file(filename) case filename when /\.(js|json)$/ r = FFI_Yajl::Parser.parse(IO.read(filename)) # Chef::DataBagItem doesn't work well with the json_create method if @klass == Chef::DataBagItem r else @klass.json_create(r) end when /\.rb$/ r = klass.new r.from_file(filename) r else ui.fatal("File must end in .js, .json, or .rb") exit 30 end end def file_exists_and_is_readable?(file) File.exist?(file) && File.readable?(file) end end end end end chef-12.3.0/lib/chef/knife/core/status_presenter.rb0000644000004100000410000001260512520074675022204 0ustar www-datawww-data# # Author:: Nicolas DUPEUX () # 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 'chef/knife/core/text_formatter' require 'chef/knife/core/generic_presenter' class Chef class Knife module Core # This module may be included into a knife subcommand class to automatically # add configuration options used by the StatusPresenter module StatusFormattingOptions # :nodoc: # Would prefer to do this in a rational way, but can't be done b/c of # Mixlib::CLI's design :( def self.included(includer) includer.class_eval do option :medium_output, :short => '-m', :long => '--medium', :boolean => true, :default => false, :description => 'Include normal attributes in the output' option :long_output, :short => '-l', :long => '--long', :boolean => true, :default => false, :description => 'Include all attributes in the output' end end end #==Chef::Knife::Core::StatusPresenter # A customized presenter for Chef::Node objects. Supports variable-length # output formats for displaying node data class StatusPresenter < GenericPresenter def format(data) if parse_format_option == :json summarize_json(data) else super end end def summarize_json(list) result_list = [] list.each do |node| result = {} result["name"] = node["name"] || node.name result["chef_environment"] = node["chef_environment"] ip = (node["ec2"] && node["ec2"]["public_ipv4"]) || node["ipaddress"] fqdn = (node["ec2"] && node["ec2"]["public_hostname"]) || node["fqdn"] result["ip"] = ip if ip result["fqdn"] = fqdn if fqdn result["run_list"] = node.run_list if config["run_list"] result["ohai_time"] = node["ohai_time"] result["platform"] = node["platform"] if node["platform"] result["platform_version"] = node["platform_version"] if node["platform_version"] if config[:long_output] result["default"] = node.default_attrs result["override"] = node.override_attrs result["automatic"] = node.automatic_attrs end result_list << result end Chef::JSONCompat.to_json_pretty(result_list) end # Converts a Chef::Node object to a string suitable for output to a # terminal. If config[:medium_output] or config[:long_output] are set # the volume of output is adjusted accordingly. Uses colors if enabled # in the ui object. def summarize(list) summarized='' list.each do |data| node = data # special case ec2 with their split horizon whatsis. ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress] fqdn = (node[:ec2] && node[:ec2][:public_hostname]) || node[:fqdn] name = node['name'] || node.name hours, minutes, _ = time_difference_in_hms(node["ohai_time"]) hours_text = "#{hours} hour#{hours == 1 ? ' ' : 's'}" minutes_text = "#{minutes} minute#{minutes == 1 ? ' ' : 's'}" run_list = "#{node['run_list']}" if config[:run_list] if hours > 24 color = :red text = hours_text elsif hours >= 1 color = :yellow text = hours_text else color = :green text = minutes_text end line_parts = Array.new line_parts << @ui.color(text, color) + ' ago' << name line_parts << fqdn if fqdn line_parts << ip if ip line_parts << run_list if run_list if node['platform'] platform = node['platform'] if node['platform_version'] platform << " #{node['platform_version']}" end line_parts << platform end summarized=summarized + line_parts.join(', ') + ".\n" end summarized end def key(key_text) ui.color(key_text, :cyan) end # :nodoc: # TODO: this is duplicated from StatusHelper in the Webui. dedup. def time_difference_in_hms(unix_time) now = Time.now.to_i difference = now - unix_time.to_i hours = (difference / 3600).to_i difference = difference % 3600 minutes = (difference / 60).to_i seconds = (difference % 60) return [hours, minutes, seconds] end end end end end chef-12.3.0/lib/chef/knife/core/node_editor.rb0000644000004100000410000000660012520074675021063 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/json_compat' require 'chef/node' require 'tempfile' class Chef class Knife class NodeEditor attr_reader :node attr_reader :ui attr_reader :config def initialize(node, ui, config) @node, @ui, @config = node, ui, config end def edit_node abort "You specified the --disable_editing option, nothing to edit" if config[:disable_editing] assert_editor_set! updated_node_data = @ui.edit_data(view) apply_updates(updated_node_data) @updated_node end def updated? pristine_copy = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(node)) updated_copy = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(@updated_node)) unless pristine_copy == updated_copy updated_properties = %w{name normal chef_environment run_list default override automatic}.reject do |key| pristine_copy[key] == updated_copy[key] end end ( pristine_copy != updated_copy ) && updated_properties end private def view result = {} result["name"] = node.name result["chef_environment"] = node.chef_environment result["normal"] = node.normal_attrs result["run_list"] = node.run_list if config[:all_attributes] result["default"] = node.default_attrs result["override"] = node.override_attrs result["automatic"] = node.automatic_attrs end result end def apply_updates(updated_data) if node.name and node.name != updated_data["name"] ui.warn "Changing the name of a node results in a new node being created, #{node.name} will not be modified or removed." confirm = ui.confirm "Proceed with creation of new node" end @updated_node = Node.new.tap do |n| n.name( updated_data["name"] ) n.chef_environment( updated_data["chef_environment"] ) n.run_list( updated_data["run_list"]) n.normal_attrs = updated_data["normal"] if config[:all_attributes] n.default_attrs = updated_data["default"] n.override_attrs = updated_data["override"] n.automatic_attrs = updated_data["automatic"] else n.default_attrs = node.default_attrs n.override_attrs = node.override_attrs n.automatic_attrs = node.automatic_attrs end end end def abort(message) ui.error(message) exit 1 end def assert_editor_set! unless config[:editor] abort "You must set your EDITOR environment variable or configure your editor via knife.rb" end end end end end chef-12.3.0/lib/chef/knife/core/text_formatter.rb0000644000004100000410000000524612520074675021644 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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. # class Chef class Knife module Core class TextFormatter attr_reader :data attr_reader :ui def initialize(data, ui) @ui = ui @data = if data.respond_to?(:display_hash) data.display_hash elsif data.kind_of?(Array) data elsif data.respond_to?(:to_hash) data.to_hash else data end end def formatted_data @formatted_data ||= text_format(data) end def text_format(data) buffer = '' if data.respond_to?(:keys) justify_width = data.keys.map {|k| k.to_s.size }.max.to_i + 1 data.sort.each do |key, value| # key: ['value'] should be printed as key: value if value.kind_of?(Array) && value.size == 1 && is_singleton(value[0]) value = value[0] end if is_singleton(value) # Strings are printed as key: value. justified_key = ui.color("#{key}:".ljust(justify_width), :cyan) buffer << "#{justified_key} #{value}\n" else # Arrays and hashes get indented on their own lines. buffer << ui.color("#{key}:\n", :cyan) lines = text_format(value).split("\n") lines.each { |line| buffer << " #{line}\n" } end end elsif data.kind_of?(Array) data.each_index do |index| item = data[index] buffer << text_format(data[index]) # Separate items with newlines if it's an array of hashes or an # array of arrays buffer << "\n" if !is_singleton(data[index]) && index != data.size-1 end else buffer << "#{data}\n" end buffer end def is_singleton(value) !(value.kind_of?(Array) || value.respond_to?(:keys)) end end end end end chef-12.3.0/lib/chef/knife/core/subcommand_loader.rb0000644000004100000410000001741612520074675022255 0ustar www-datawww-data# Author:: Christopher Brown () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 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 'chef/version' require 'chef/util/path_helper' class Chef class Knife class SubcommandLoader MATCHES_CHEF_GEM = %r{/chef-[\d]+\.[\d]+\.[\d]+}.freeze MATCHES_THIS_CHEF_GEM = %r{/chef-#{Chef::VERSION}/}.freeze attr_reader :chef_config_dir attr_reader :env def initialize(chef_config_dir, env=nil) @chef_config_dir = chef_config_dir @forced_activate = {} # Deprecated and un-used instance variable. @env = env unless env.nil? Chef::Log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.") end end # Load all the sub-commands def load_commands subcommand_files.each { |subcommand| Kernel.load subcommand } true end # Returns an Array of paths to knife commands located in chef_config_dir/plugins/knife/ # and ~/.chef/plugins/knife/ def site_subcommands user_specific_files = [] if chef_config_dir user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", Chef::Util::PathHelper.escape_glob(chef_config_dir))) end # finally search ~/.chef/plugins/knife/*.rb Chef::Util::PathHelper.home('.chef', 'plugins', 'knife') do |p| user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(p), '*.rb')) end user_specific_files end # Returns a Hash of paths to knife commands built-in to chef, or installed via gem. # If rubygems is not installed, falls back to globbing the knife directory. # The Hash is of the form {"relative/path" => "/absolute/path"} #-- # Note: the "right" way to load the plugins is to require the relative path, i.e., # require 'chef/knife/command' # but we're getting frustrated by bugs at every turn, and it's slow besides. So # subcommand loader has been modified to load the plugins by using Kernel.load # with the absolute path. def gem_and_builtin_subcommands if have_plugin_manifest? find_subcommands_via_manifest else # search all gems for chef/knife/*.rb require 'rubygems' find_subcommands_via_rubygems end rescue LoadError find_subcommands_via_dirglob end def subcommand_files @subcommand_files ||= (gem_and_builtin_subcommands.values + site_subcommands).flatten.uniq end # If the user has created a ~/.chef/plugin_manifest.json file, we'll use # that instead of inspecting the on-system gems to find the plugins. The # file format is expected to look like: # # { "plugins": { # "knife-ec2": { # "paths": [ # "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_create.rb", # "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_delete.rb" # ] # } # } # } # # Extraneous content in this file is ignored. This intentional so that we # can adapt the file format for potential behavior changes to knife in # the future. def find_subcommands_via_manifest # Format of subcommand_files is "relative_path" (something you can # Kernel.require()) => full_path. The relative path isn't used # currently, so we just map full_path => full_path. subcommand_files = {} plugin_manifest["plugins"].each do |plugin_name, plugin_manifest| plugin_manifest["paths"].each do |cmd_path| subcommand_files[cmd_path] = cmd_path end end subcommand_files.merge(find_subcommands_via_dirglob) end def find_subcommands_via_dirglob # The "require paths" of the core knife subcommands bundled with chef files = Dir[File.join(Chef::Util::PathHelper.escape_glob(File.expand_path('../../../knife', __FILE__)), '*.rb')] subcommand_files = {} files.each do |knife_file| rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1] subcommand_files[rel_path] = knife_file end subcommand_files end def find_subcommands_via_rubygems files = find_files_latest_gems 'chef/knife/*.rb' subcommand_files = {} files.each do |file| rel_path = file[/(#{Regexp.escape File.join('chef', 'knife', '')}.*)\.rb/, 1] # When not installed as a gem (ChefDK/appbundler in particular), AND # a different version of Chef is installed via gems, `files` will # include some files from the 'other' Chef install. If this contains # a knife command that doesn't exist in this version of Chef, we will # get a LoadError later when we try to require it. next if from_different_chef_version?(file) subcommand_files[rel_path] = file end subcommand_files.merge(find_subcommands_via_dirglob) end def have_plugin_manifest? plugin_manifest_path && File.exist?(plugin_manifest_path) end def plugin_manifest Chef::JSONCompat.from_json(File.read(plugin_manifest_path)) end def plugin_manifest_path Chef::Util::PathHelper.home('.chef', 'plugin_manifest.json') end private def find_files_latest_gems(glob, check_load_path=true) files = [] if check_load_path files = $LOAD_PATH.map { |load_path| Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob(load_path)}#{Gem.suffix_pattern}"] }.flatten.select { |file| File.file? file.untaint } end gem_files = latest_gem_specs.map do |spec| # Gem::Specification#matches_for_glob wasn't added until RubyGems 1.8 if spec.respond_to? :matches_for_glob spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}") else check_spec_for_glob(spec, glob) end end.flatten files.concat gem_files files.uniq! if check_load_path return files end def latest_gem_specs @latest_gem_specs ||= if Gem::Specification.respond_to? :latest_specs Gem::Specification.latest_specs(true) # find prerelease gems else Gem.source_index.latest_specs(true) end end def check_spec_for_glob(spec, glob) dirs = if spec.require_paths.size > 1 then "{#{spec.require_paths.join(',')}}" else spec.require_paths.first end glob = File.join(Chef::Util::PathHelper.escape_glob(spec.full_gem_path, dirs), glob) Dir[glob].map { |f| f.untaint } end def from_different_chef_version?(path) matches_any_chef_gem?(path) && !matches_this_chef_gem?(path) end def matches_any_chef_gem?(path) path =~ MATCHES_CHEF_GEM end def matches_this_chef_gem?(path) path =~ MATCHES_THIS_CHEF_GEM end end end end chef-12.3.0/lib/chef/knife/core/ui.rb0000644000004100000410000002010012520074675017174 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 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 'forwardable' require 'chef/platform/query_helpers' require 'chef/knife/core/generic_presenter' require 'tempfile' class Chef class Knife #==Chef::Knife::UI # The User Interaction class used by knife. class UI extend Forwardable attr_reader :stdout attr_reader :stderr attr_reader :stdin attr_reader :config attr_reader :presenter def_delegator :@presenter, :format_list_for_display def_delegator :@presenter, :format_for_display def_delegator :@presenter, :format_cookbook_list_for_display def initialize(stdout, stderr, stdin, config) @stdout, @stderr, @stdin, @config = stdout, stderr, stdin, config @presenter = Chef::Knife::Core::GenericPresenter.new(self, config) end # Creates a new +presenter_class+ object and uses it to format structured # data for display. By default, a Chef::Knife::Core::GenericPresenter # object is used. def use_presenter(presenter_class) @presenter = presenter_class.new(self, config) end def highline @highline ||= begin require 'highline' HighLine.new end end # Prints a message to stdout. Aliased as +info+ for compatibility with # the logger API. def msg(message) begin stdout.puts message rescue Errno::EPIPE => e raise e if @config[:verbosity] >= 2 exit 0 end end # Prints a msg to stderr. Used for info, warn, error, and fatal. def log(message) begin stderr.puts message rescue Errno::EPIPE => e raise e if @config[:verbosity] >= 2 exit 0 end end alias :info :log alias :err :log # Print a warning message def warn(message) log("#{color('WARNING:', :yellow, :bold)} #{message}") end # Print an error message def error(message) log("#{color('ERROR:', :red, :bold)} #{message}") end # Print a message describing a fatal error. def fatal(message) log("#{color('FATAL:', :red, :bold)} #{message}") end def color(string, *colors) if color? highline.color(string, *colors) else string end end # Should colored output be used? For output to a terminal, this is # determined by the value of `config[:color]`. When output is not to a # terminal, colored output is never used def color? Chef::Config[:color] && stdout.tty? end def ask(*args, &block) highline.ask(*args, &block) end def list(*args) highline.list(*args) end # Formats +data+ using the configured presenter and outputs the result # via +msg+. Formatting can be customized by configuring a different # presenter. See +use_presenter+ def output(data) msg @presenter.format(data) end # Determines if the output format is a data interchange format, i.e., # JSON or YAML def interchange? @presenter.interchange? end def ask_question(question, opts={}) question = question + "[#{opts[:default]}] " if opts[:default] if opts[:default] and config[:defaults] opts[:default] else stdout.print question a = stdin.readline.strip if opts[:default] a.empty? ? opts[:default] : a else a end end end def pretty_print(data) begin stdout.puts data rescue Errno::EPIPE => e raise e if @config[:verbosity] >= 2 exit 0 end end # Hash -> Hash # Works the same as edit_data but # returns a hash rather than a JSON string/Fully infated object def edit_hash(hash) raw = edit_data(hash, false) Chef::JSONCompat.parse(raw) end def edit_data(data, parse_output=true) output = Chef::JSONCompat.to_json_pretty(data) if (!config[:disable_editing]) Tempfile.open([ 'knife-edit-', '.json' ]) do |tf| tf.sync = true tf.puts output tf.close raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}") output = IO.read(tf.path) end end parse_output ? Chef::JSONCompat.from_json(output) : output end def edit_object(klass, name) object = klass.load(name) output = edit_data(object) # Only make the save if the user changed the object. # # Output JSON for the original (object) and edited (output), then parse # them without reconstituting the objects into real classes # (create_additions=false). Then, compare the resulting simple objects, # which will be Array/Hash/String/etc. # # We wouldn't have to do these shenanigans if all the editable objects # implemented to_hash, or if to_json against a hash returned a string # with stable key order. object_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(object)) output_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(output)) if object_parsed_again != output_parsed_again output.save self.msg("Saved #{output}") else self.msg("Object unchanged, not saving") end output(format_for_display(object)) if config[:print_after] end def confirmation_instructions(default_choice) case default_choice when true '? (Y/n) ' when false '? (y/N) ' else '? (Y/N) ' end end # See confirm method for argument information def confirm_without_exit(question, append_instructions=true, default_choice=nil) return true if config[:yes] stdout.print question stdout.print confirmation_instructions(default_choice) if append_instructions answer = stdin.readline answer.chomp! case answer when "Y", "y" true when "N", "n" self.msg("You said no, so I'm done here.") false when "" unless default_choice.nil? default_choice else self.msg("I have no idea what to do with '#{answer}'") self.msg("Just say Y or N, please.") confirm_without_exit(question, append_instructions, default_choice) end else self.msg("I have no idea what to do with '#{answer}'") self.msg("Just say Y or N, please.") confirm_without_exit(question, append_instructions, default_choice) end end # # Not the ideal signature for a function but we need to stick with this # for now until we get a chance to break our API in Chef 12. # # question => Question to print before asking for confirmation # append_instructions => Should print '? (Y/N)' as instructions # default_choice => Set to true for 'Y', and false for 'N' as default answer # def confirm(question, append_instructions=true, default_choice=nil) unless confirm_without_exit(question, append_instructions, default_choice) exit 3 end true end end end end chef-12.3.0/lib/chef/knife/core/cookbook_scm_repo.rb0000644000004100000410000001175712520074675022276 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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 'chef/mixin/shell_out' class Chef class Knife class CookbookSCMRepo DIRTY_REPO = /^[\s]+M/ include Chef::Mixin::ShellOut attr_reader :repo_path attr_reader :default_branch attr_reader :use_current_branch attr_reader :ui def initialize(repo_path, ui, opts={}) @repo_path = repo_path @ui = ui @default_branch = 'master' @use_current_branch = false apply_opts(opts) end def sanity_check unless ::File.directory?(repo_path) ui.error("The cookbook repo path #{repo_path} does not exist or is not a directory") exit 1 end unless git_repo?(repo_path) ui.error "The cookbook repo #{repo_path} is not a git repository." ui.info("Use `git init` to initialize a git repo") exit 1 end if use_current_branch @default_branch = get_current_branch() end unless branch_exists?(default_branch) ui.error "The default branch '#{default_branch}' does not exist" ui.info "If this is a new git repo, make sure you have at least one commit before installing cookbooks" exit 1 end cmd = git('status --porcelain') if cmd.stdout =~ DIRTY_REPO ui.error "You have uncommitted changes to your cookbook repo (#{repo_path}):" ui.msg cmd.stdout ui.info "Commit or stash your changes before importing cookbooks" exit 1 end # TODO: any untracked files in the cookbook directory will get nuked later # make this an error condition also. true end def reset_to_default_state ui.info("Checking out the #{default_branch} branch.") git("checkout #{default_branch}") end def prepare_to_import(cookbook_name) branch = "chef-vendor-#{cookbook_name}" if branch_exists?(branch) ui.info("Pristine copy branch (#{branch}) exists, switching to it.") git("checkout #{branch}") else ui.info("Creating pristine copy branch #{branch}") git("checkout -b #{branch}") end end def finalize_updates_to(cookbook_name, version) if update_count = updated?(cookbook_name) ui.info "#{update_count} files updated, committing changes" git("add #{cookbook_name}") git("commit -m \"Import #{cookbook_name} version #{version}\" -- #{cookbook_name}") ui.info("Creating tag cookbook-site-imported-#{cookbook_name}-#{version}") git("tag -f cookbook-site-imported-#{cookbook_name}-#{version}") true else ui.info("No changes made to #{cookbook_name}") false end end def merge_updates_from(cookbook_name, version) branch = "chef-vendor-#{cookbook_name}" Dir.chdir(repo_path) do if system("git merge #{branch}") ui.info("Cookbook #{cookbook_name} version #{version} successfully installed") else ui.error("You have merge conflicts - please resolve manually") ui.info("Merge status (cd #{repo_path}; git status):") system("git status") exit 3 end end end def updated?(cookbook_name) update_count = git("status --porcelain -- #{cookbook_name}").stdout.strip.lines.count update_count == 0 ? nil : update_count end def branch_exists?(branch_name) git("branch --no-color").stdout.lines.any? {|l| l =~ /\s#{Regexp.escape(branch_name)}(?:\s|$)/ } end def get_current_branch() ref = git("symbolic-ref HEAD").stdout ref.chomp.split('/')[2] end private def git_repo?(directory) if File.directory?(File.join(directory, '.git')) return true elsif File.dirname(directory) == directory return false else git_repo?(File.dirname(directory)) end end def apply_opts(opts) opts.each do |option, value| case option.to_s when 'default_branch' @default_branch = value when 'use_current_branch' @use_current_branch = value end end end def git(command) shell_out!("git #{command}", :cwd => repo_path) end end end end chef-12.3.0/lib/chef/knife/bootstrap.rb0000644000004100000410000003455612520074675017670 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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' require 'chef/knife/data_bag_secret_options' require 'erubis' require 'chef/knife/bootstrap/chef_vault_handler' require 'chef/knife/bootstrap/client_builder' require 'chef/util/path_helper' class Chef class Knife class Bootstrap < Knife include DataBagSecretOptions attr_accessor :client_builder attr_accessor :chef_vault_handler deps do require 'chef/knife/core/bootstrap_context' require 'chef/json_compat' require 'tempfile' require 'highline' require 'net/ssh' require 'net/ssh/multi' require 'chef/knife/ssh' Chef::Knife::Ssh.load_deps end banner "knife bootstrap FQDN (options)" option :ssh_user, :short => "-x USERNAME", :long => "--ssh-user USERNAME", :description => "The ssh username", :default => "root" option :ssh_password, :short => "-P PASSWORD", :long => "--ssh-password PASSWORD", :description => "The ssh password" option :ssh_port, :short => "-p PORT", :long => "--ssh-port PORT", :description => "The ssh port", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key } option :ssh_gateway, :short => "-G GATEWAY", :long => "--ssh-gateway GATEWAY", :description => "The ssh gateway", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key } option :forward_agent, :short => "-A", :long => "--forward-agent", :description => "Enable SSH agent forwarding", :boolean => true option :identity_file, :short => "-i IDENTITY_FILE", :long => "--identity-file IDENTITY_FILE", :description => "The SSH identity file used for authentication" option :chef_node_name, :short => "-N NAME", :long => "--node-name NAME", :description => "The Chef node name for your new node" option :prerelease, :long => "--prerelease", :description => "Install the pre-release chef gems" option :bootstrap_version, :long => "--bootstrap-version VERSION", :description => "The version of Chef to install", :proc => lambda { |v| Chef::Config[:knife][:bootstrap_version] = v } option :bootstrap_proxy, :long => "--bootstrap-proxy PROXY_URL", :description => "The proxy server for the node being bootstrapped", :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p } option :bootstrap_no_proxy, :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]", :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode", :proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np } # DEPR: Remove this option in Chef 13 option :distro, :short => "-d DISTRO", :long => "--distro DISTRO", :description => "Bootstrap a distro using a template. [DEPRECATED] Use -t / --bootstrap-template option instead.", :proc => Proc.new { |v| Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use -t / --bootstrap-template option instead.") v } option :bootstrap_template, :short => "-t TEMPLATE", :long => "--bootstrap-template TEMPLATE", :description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates." option :use_sudo, :long => "--sudo", :description => "Execute the bootstrap via sudo", :boolean => true option :use_sudo_password, :long => "--use-sudo-password", :description => "Execute the bootstrap via sudo with password", :boolean => false # DEPR: Remove this option in Chef 13 option :template_file, :long => "--template-file TEMPLATE", :description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.", :proc => Proc.new { |v| Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.") v } option :run_list, :short => "-r RUN_LIST", :long => "--run-list RUN_LIST", :description => "Comma separated list of roles/recipes to apply", :proc => lambda { |o| o.split(/[\s,]+/) }, :default => [] option :first_boot_attributes, :short => "-j JSON_ATTRIBS", :long => "--json-attributes", :description => "A JSON string to be added to the first run of chef-client", :proc => lambda { |o| Chef::JSONCompat.parse(o) }, :default => {} option :host_key_verify, :long => "--[no-]host-key-verify", :description => "Verify host key, enabled by default.", :boolean => true, :default => true option :hint, :long => "--hint HINT_NAME[=HINT_FILE]", :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.", :proc => Proc.new { |h| Chef::Config[:knife][:hints] ||= Hash.new name, path = h.split("=") Chef::Config[:knife][:hints][name] = path ? Chef::JSONCompat.parse(::File.read(path)) : Hash.new } option :bootstrap_url, :long => "--bootstrap-url URL", :description => "URL to a custom installation script", :proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u } option :bootstrap_install_command, :long => "--bootstrap-install-command COMMANDS", :description => "Custom command to install chef-client", :proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic } option :bootstrap_wget_options, :long => "--bootstrap-wget-options OPTIONS", :description => "Add options to wget when installing chef-client", :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo } option :bootstrap_curl_options, :long => "--bootstrap-curl-options OPTIONS", :description => "Add options to curl when install chef-client", :proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co } option :node_ssl_verify_mode, :long => "--node-ssl-verify-mode [peer|none]", :description => "Whether or not to verify the SSL cert for all HTTPS requests.", :proc => Proc.new { |v| valid_values = ["none", "peer"] unless valid_values.include?(v) raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}" end } option :node_verify_api_cert, :long => "--[no-]node-verify-api-cert", :description => "Verify the SSL cert for HTTPS requests to the Chef server API.", :boolean => true option :bootstrap_vault_file, :long => '--bootstrap-vault-file VAULT_FILE', :description => 'A JSON file with a list of vault(s) and item(s) to be updated' option :bootstrap_vault_json, :long => '--bootstrap-vault-json VAULT_JSON', :description => 'A JSON string with the vault(s) and item(s) to be updated' option :bootstrap_vault_item, :long => '--bootstrap-vault-item VAULT_ITEM', :description => 'A single vault and item to update as "vault:item"', :proc => Proc.new { |i| (vault, item) = i.split(/:/) Chef::Config[:knife][:bootstrap_vault_item] ||= {} Chef::Config[:knife][:bootstrap_vault_item][vault] ||= [] Chef::Config[:knife][:bootstrap_vault_item][vault].push(item) Chef::Config[:knife][:bootstrap_vault_item] } def initialize(argv=[]) super @client_builder = Chef::Knife::Bootstrap::ClientBuilder.new( chef_config: Chef::Config, knife_config: config, ui: ui, ) @chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new( knife_config: config, ui: ui ) end # The default bootstrap template to use to bootstrap a server This is a public API hook # which knife plugins use or inherit and override. # # @return [String] Default bootstrap template def default_bootstrap_template "chef-full" end # The server_name is the DNS or IP we are going to connect to, it is not necessarily # the node name, the fqdn, or the hostname of the server. This is a public API hook # which knife plugins use or inherit and override. # # @return [String] The DNS or IP that bootstrap will connect to def server_name Array(@name_args).first end def bootstrap_template # The order here is important. We want to check if we have the new Chef 12 option is set first. # Knife cloud plugins unfortunately all set a default option for the :distro so it should be at # the end. config[:bootstrap_template] || config[:template_file] || config[:distro] || default_bootstrap_template end def find_template template = bootstrap_template # Use the template directly if it's a path to an actual file if File.exists?(template) Chef::Log.debug("Using the specified bootstrap template: #{File.dirname(template)}") return template end # Otherwise search the template directories until we find the right one bootstrap_files = [] bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap/templates', "#{template}.erb") bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir Chef::Util::PathHelper.home('.chef', 'bootstrap', "#{template}.erb") {|p| bootstrap_files << p} bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{template}.erb")) bootstrap_files.flatten! template_file = Array(bootstrap_files).find do |bootstrap_template| Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}") File.exists?(bootstrap_template) end unless template_file ui.info("Can not find bootstrap definition for #{template}") raise Errno::ENOENT end Chef::Log.debug("Found bootstrap template in #{File.dirname(template_file)}") template_file end def secret @secret ||= encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil end def bootstrap_context @bootstrap_context ||= Knife::Core::BootstrapContext.new( config, config[:run_list], Chef::Config, secret ) end def render_template template_file = find_template template = IO.read(template_file).chomp Erubis::Eruby.new(template).evaluate(bootstrap_context) end def run validate_name_args! $stdout.sync = true # chef-vault integration must use the new client-side hawtness, otherwise to use the # new client-side hawtness, just delete your validation key. if chef_vault_handler.doing_chef_vault? || (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key]))) client_builder.run chef_vault_handler.run(node_name: config[:chef_node_name]) bootstrap_context.client_pem = client_builder.client_path else ui.info("Doing old-style registration with the validation key at #{Chef::Config[:validation_key]}...") ui.info("Delete your validation key in order to use your user credentials instead") ui.info("") end ui.info("Connecting to #{ui.color(server_name, :bold)}") begin knife_ssh.run rescue Net::SSH::AuthenticationFailed if config[:ssh_password] raise else ui.info("Failed to authenticate #{config[:ssh_user]} - trying password auth") knife_ssh_with_password_auth.run end end end def validate_name_args! if server_name.nil? ui.error("Must pass an FQDN or ip to bootstrap") exit 1 elsif server_name == "windows" # catches "knife bootstrap windows" when that command is not installed ui.warn("Hostname containing 'windows' specified. Please install 'knife-windows' if you are attempting to bootstrap a Windows node via WinRM.") end end def knife_ssh ssh = Chef::Knife::Ssh.new ssh.ui = ui ssh.name_args = [ server_name, ssh_command ] ssh.config[:ssh_user] = config[:ssh_user] ssh.config[:ssh_password] = config[:ssh_password] ssh.config[:ssh_port] = config[:ssh_port] ssh.config[:ssh_gateway] = config[:ssh_gateway] ssh.config[:forward_agent] = config[:forward_agent] ssh.config[:identity_file] = config[:identity_file] ssh.config[:manual] = true ssh.config[:host_key_verify] = config[:host_key_verify] ssh.config[:on_error] = :raise ssh end def knife_ssh_with_password_auth ssh = knife_ssh ssh.config[:identity_file] = nil ssh.config[:ssh_password] = ssh.get_password ssh end def ssh_command command = render_template if config[:use_sudo] command = config[:use_sudo_password] ? "echo '#{config[:ssh_password]}' | sudo -SH #{command}" : "sudo -H #{command}" end command end end end end chef-12.3.0/lib/chef/knife/role_run_list_set.rb0000644000004100000410000000421112520074675021367 0ustar www-datawww-data# # Author:: Mike Fiedler () # Author:: William Albenzi () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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 RoleRunListSet < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role run_list set [ROLE] [ENTRIES]" # Clears out any existing env_run_list_items and sets them to the # specified entries def set_env_run_list(role, environment, entries) nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end entries.each { |e| nlist << e } role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = "_default" if @name_args.size < 1 ui.fatal "You must supply both a role name and an environment run list." show_usage exit 1 elsif @name_args.size > 1 # Check for nested lists and create a single plain one entries = @name_args[1..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[1].split(',').map { |e| e.strip } end set_env_run_list(role, environment, entries ) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/role_create.rb0000644000004100000410000000257612520074675020134 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleCreate < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role create ROLE (options)" option :description, :short => "-d DESC", :long => "--description DESC", :description => "The role description" def run @role_name = @name_args[0] if @role_name.nil? show_usage ui.fatal("You must specify a role name") exit 1 end role = Chef::Role.new role.name(@role_name) role.description(config[:description]) if config[:description] create_object(role) end end end end chef-12.3.0/lib/chef/knife/data_bag_show.rb0000644000004100000410000000461012520074675020421 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2009-2010 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' require 'chef/knife/data_bag_secret_options' class Chef class Knife class DataBagShow < Knife include DataBagSecretOptions deps do require 'chef/data_bag' require 'chef/encrypted_data_bag_item' end banner "knife data bag show BAG [ITEM] (options)" category "data bag" def run display = case @name_args.length when 2 # Bag and Item names provided secret = encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil raw_data = Chef::DataBagItem.load(@name_args[0], @name_args[1]).raw_data encrypted = encrypted?(raw_data) if encrypted && secret # Users do not need to pass --encrypt to read data, we simply try to use the provided secret ui.info("Encrypted data bag detected, decrypting with provided secret.") raw = Chef::EncryptedDataBagItem.load(@name_args[0], @name_args[1], secret) format_for_display(raw.to_hash) elsif encrypted && !secret ui.warn("Encrypted data bag detected, but no secret provided for decoding. Displaying encrypted data.") format_for_display(raw_data) else ui.info("Unencrypted data bag detected, ignoring any provided secret options.") format_for_display(raw_data) end when 1 # Only Bag name provided format_list_for_display(Chef::DataBag.load(@name_args[0])) else stdout.puts opt_parser exit(1) end output(display) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_share.rb0000644000004100000410000001402712520074675021656 0ustar www-datawww-data# Author:: Nuo Yan () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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' require 'chef/mixin/shell_out' class Chef class Knife class CookbookSiteShare < Knife include Chef::Mixin::ShellOut deps do require 'chef/cookbook_loader' require 'chef/cookbook_uploader' require 'chef/cookbook_site_streaming_uploader' require 'mixlib/shellout' end include Chef::Mixin::ShellOut banner "knife cookbook site share COOKBOOK [CATEGORY] (options)" category "cookbook site" option :cookbook_path, :short => "-o PATH:PATH", :long => "--cookbook-path PATH:PATH", :description => "A colon-separated path to look for cookbooks in", :proc => lambda { |o| Chef::Config.cookbook_path = o.split(":") } option :dry_run, :long => '--dry-run', :short => '-n', :boolean => true, :default => false, :description => "Don't take action, only print what files will be upload to SuperMarket." def run config[:cookbook_path] ||= Chef::Config[:cookbook_path] if @name_args.length < 1 show_usage ui.fatal("You must specify the cookbook name.") exit(1) elsif @name_args.length < 2 cookbook_name = @name_args[0] category = get_category(cookbook_name) else cookbook_name = @name_args[0] category = @name_args[1] end cl = Chef::CookbookLoader.new(config[:cookbook_path]) if cl.cookbook_exists?(cookbook_name) cookbook = cl[cookbook_name] Chef::CookbookUploader.new(cookbook).validate_cookbooks tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook) begin Chef::Log.debug("Temp cookbook directory is #{tmp_cookbook_dir.inspect}") ui.info("Making tarball #{cookbook_name}.tgz") shell_out!("#{tar_cmd} -czf #{cookbook_name}.tgz #{cookbook_name}", :cwd => tmp_cookbook_dir) rescue => e ui.error("Error making tarball #{cookbook_name}.tgz: #{e.message}. Increase log verbosity (-VV) for more information.") Chef::Log.debug("\n#{e.backtrace.join("\n")}") exit(1) end if config[:dry_run] ui.info("Not uploading #{cookbook_name}.tgz due to --dry-run flag.") result = shell_out!("#{tar_cmd} -tzf #{cookbook_name}.tgz", :cwd => tmp_cookbook_dir) ui.info(result.stdout) FileUtils.rm_rf tmp_cookbook_dir return end begin do_upload("#{tmp_cookbook_dir}/#{cookbook_name}.tgz", category, Chef::Config[:node_name], Chef::Config[:client_key]) ui.info("Upload complete!") Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}") FileUtils.rm_rf tmp_cookbook_dir rescue => e ui.error("Error uploading cookbook #{cookbook_name} to the Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.") Chef::Log.debug("\n#{e.backtrace.join("\n")}") exit(1) end else ui.error("Could not find cookbook #{cookbook_name} in your cookbook path.") exit(1) end end def get_category(cookbook_name) begin data = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}") if !data["category"] && data["error_code"] ui.fatal("Received an error from the Opscode Cookbook site: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.") exit(1) else data['category'] end rescue => e ui.fatal("Unable to reach Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.") Chef::Log.debug("\n#{e.backtrace.join("\n")}") exit(1) end end def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename) uri = "https://supermarket.chef.io/api/v1/cookbooks" category_string = Chef::JSONCompat.to_json({ 'category'=>cookbook_category }) http_resp = Chef::CookbookSiteStreamingUploader.post(uri, user_id, user_secret_filename, { :tarball => File.open(cookbook_filename), :cookbook => category_string }) res = Chef::JSONCompat.from_json(http_resp.body) if http_resp.code.to_i != 201 if res['error_messages'] if res['error_messages'][0] =~ /Version already exists/ ui.error "The same version of this cookbook already exists on the Opscode Cookbook Site." exit(1) else ui.error "#{res['error_messages'][0]}" exit(1) end else ui.error "Unknown error while sharing cookbook" ui.error "Server response: #{http_resp.body}" exit(1) end end res end def tar_cmd if !@tar_cmd @tar_cmd = "tar" begin # Unix and Mac only - prefer gnutar if shell_out("which gnutar").exitstatus.equal?(0) @tar_cmd = "gnutar" end rescue Errno::ENOENT end end @tar_cmd end end end end chef-12.3.0/lib/chef/knife/tag_create.rb0000644000004100000410000000263512520074675017742 0ustar www-datawww-data# # Author:: Ryan Davis () # Author:: Daniel DeLeo () # Author:: Nuo Yan () # Copyright:: Copyright (c) 2011 Ryan Davis and 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 TagCreate < Knife deps do require 'chef/node' end banner "knife tag create NODE TAG ..." def run name = @name_args[0] tags = @name_args[1..-1] if name.nil? || tags.nil? || tags.empty? show_usage ui.fatal("You must specify a node name and at least one tag.") exit 1 end node = Chef::Node.load name tags.each do |tag| (node.tags << tag).uniq! end node.save ui.info("Created tags #{tags.join(", ")} for node #{name}.") end end end end chef-12.3.0/lib/chef/knife/user_show.rb0000644000004100000410000000233112520074675017653 0ustar www-datawww-data# # Author:: Steven Danna () # 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 include Knife::Core::MultiAttributeReturnOption deps do require 'chef/user' require 'chef/json_compat' end banner "knife user show 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 user = Chef::User.load(@user_name) output(format_for_display(user)) end end end end chef-12.3.0/lib/chef/knife/role_from_file.rb0000644000004100000410000000247612520074675020632 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleFromFile < Knife deps do require 'chef/role' require 'chef/knife/core/object_loader' require 'chef/json_compat' end banner "knife role from file FILE [FILE..] (options)" def loader @loader ||= Knife::Core::ObjectLoader.new(Chef::Role, ui) end def run @name_args.each do |arg| updated = loader.load_from("roles", arg) updated.save output(format_for_display(updated)) if config[:print_after] ui.info("Updated Role #{updated.name}!") end end end end end chef-12.3.0/lib/chef/knife/node_bulk_delete.rb0000644000004100000410000000376012520074675021130 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeBulkDelete < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node bulk delete REGEX (options)" def run if name_args.length < 1 ui.fatal("You must supply a regular expression to match the results against") exit 42 end nodes_to_delete = {} matcher = /#{name_args[0]}/ all_nodes.each do |name, node| next unless name =~ matcher nodes_to_delete[name] = node end if nodes_to_delete.empty? ui.msg "No nodes match the expression /#{name_args[0]}/" exit 0 end ui.msg("The following nodes will be deleted:") ui.msg("") ui.msg(ui.list(nodes_to_delete.keys.sort, :columns_down)) ui.msg("") ui.confirm("Are you sure you want to delete these nodes") nodes_to_delete.sort.each do |name, node| node.destroy ui.msg("Deleted node #{name}") end end def all_nodes node_uris_by_name = Chef::Node.list node_uris_by_name.keys.inject({}) do |nodes_by_name, name| nodes_by_name[name] = Chef::Node.new.tap {|n| n.name(name)} nodes_by_name end end end end end chef-12.3.0/lib/chef/knife/status.rb0000644000004100000410000000570212520074675017165 0ustar www-datawww-data# # Author:: Ian Meyer () # Copyright:: Copyright (c) 2010 Ian Meyer # 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' require 'chef/knife/core/status_presenter' require 'chef/knife/core/node_presenter' class Chef class Knife class Status < Knife include Knife::Core::NodeFormattingOptions deps do require 'chef/search/query' end banner "knife status QUERY (options)" option :run_list, :short => "-r", :long => "--run-list", :description => "Show the run list" option :sort_reverse, :short => "-s", :long => "--sort-reverse", :description => "Sort the status list by last run time descending" option :hide_healthy, :short => "-H", :long => "--hide-healthy", :description => "Hide nodes that have run chef in the last hour" def append_to_query(term) @query << " AND " unless @query.empty? @query << term end def run ui.use_presenter Knife::Core::StatusPresenter if config[:long_output] opts = {} else opts = {filter_result: { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"], ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"], platform_version: ["platform_version"], chef_environment: ["chef_environment"]}} end @query ||= "" append_to_query(@name_args[0]) if @name_args[0] append_to_query("chef_environment:#{config[:environment]}") if config[:environment] if config[:hide_healthy] time = Time.now.to_i # AND NOT is not valid lucene syntax, so don't use append_to_query @query << " " unless @query.empty? @query << "NOT ohai_time:[#{(time - 60*60).to_s} TO #{time.to_s}]" end @query = @query.empty? ? "*:*" : @query all_nodes = [] q = Chef::Search::Query.new Chef::Log.info("Sending query: #{@query}") q.search(:node, @query, opts) do |node| all_nodes << node end output(all_nodes.sort { |n1, n2| if (config[:sort_reverse] || Chef::Config[:knife][:sort_status_reverse]) (n2["ohai_time"] or 0) <=> (n1["ohai_time"] or 0) else (n1["ohai_time"] or 0) <=> (n2["ohai_time"] or 0) end }) end end end end chef-12.3.0/lib/chef/knife/data_bag_list.rb0000644000004100000410000000214412520074675020414 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 DataBagList < Knife deps do require 'chef/data_bag' end banner "knife data bag list (options)" category "data bag" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run output(format_list_for_display(Chef::DataBag.list)) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_download.rb0000644000004100000410000000606712520074675022370 0ustar www-datawww-data# Author:: Adam Jacob () # 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 CookbookSiteDownload < Knife deps do require 'fileutils' end banner "knife cookbook site download COOKBOOK [VERSION] (options)" category "cookbook site" option :file, :short => "-f FILE", :long => "--file FILE", :description => "The filename to write to" option :force, :long => "--force", :description => "Force download deprecated version" def run if current_cookbook_deprecated? message = 'DEPRECATION: This cookbook has been deprecated. ' message << "It has been replaced by #{replacement_cookbook}." ui.warn message unless config[:force] ui.warn 'Use --force to force download deprecated cookbook.' return end end download_cookbook end def version @version = desired_cookbook_data['version'] end private def cookbooks_api_url 'https://supermarket.chef.io/api/v1/cookbooks' end def current_cookbook_data @current_cookbook_data ||= begin noauth_rest.get_rest "#{cookbooks_api_url}/#{@name_args[0]}" end end def current_cookbook_deprecated? current_cookbook_data['deprecated'] == true end def desired_cookbook_data @desired_cookbook_data ||= begin uri = if @name_args.length == 1 current_cookbook_data['latest_version'] else specific_cookbook_version_url end noauth_rest.get_rest uri end end def download_cookbook ui.info "Downloading #{@name_args[0]} from the cookbooks site at version #{version} to #{download_location}" noauth_rest.sign_on_redirect = false tf = noauth_rest.get_rest desired_cookbook_data["file"], true ::FileUtils.cp tf.path, download_location ui.info "Cookbook saved: #{download_location}" end def download_location config[:file] ||= File.join Dir.pwd, "#{@name_args[0]}-#{version}.tar.gz" config[:file] end def replacement_cookbook replacement = File.basename(current_cookbook_data['replacement']) end def specific_cookbook_version_url "#{cookbooks_api_url}/#{@name_args[0]}/versions/#{@name_args[1].gsub('.', '_')}" end end end end chef-12.3.0/lib/chef/knife/user_create.rb0000644000004100000410000000473512520074675020150 0ustar www-datawww-data# # 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 '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 ui.msg user.private_key end end end end end end chef-12.3.0/lib/chef/knife/ssl_fetch.rb0000644000004100000410000001070512520074675017613 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/knife' require 'chef/config' class Chef class Knife class SslFetch < Chef::Knife deps do require 'pp' require 'socket' require 'uri' require 'openssl' end banner "knife ssl fetch [URL] (options)" def initialize(*args) super @uri = nil end def uri @uri ||= begin Chef::Log.debug("Checking SSL cert on #{given_uri}") URI.parse(given_uri) end end def given_uri (name_args[0] or Chef::Config.chef_server_url) end def host uri.host end def port uri.port end def validate_uri unless host && port invalid_uri! end rescue URI::Error invalid_uri! end def invalid_uri! ui.error("Given URI: `#{given_uri}' is invalid") show_usage exit 1 end def remote_cert_chain tcp_connection = TCPSocket.new(host, port) shady_ssl_connection = OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context) shady_ssl_connection.connect shady_ssl_connection.peer_cert_chain end def noverify_peer_ssl_context @noverify_peer_ssl_context ||= begin noverify_peer_context = OpenSSL::SSL::SSLContext.new noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE noverify_peer_context end end def cn_of(certificate) subject = certificate.subject cn_field_tuple = subject.to_a.find {|field| field[0] == "CN" } cn_field_tuple[1] end # Convert the CN of a certificate into something that will work well as a # filename. To do so, all `*` characters are converted to the string # "wildcard" and then all characters other than alphanumeric and hypen # characters are converted to underscores. # NOTE: There is some confustion about what the CN will contain when # using internationalized domain names. RFC 6125 mandates that the ascii # representation be used, but it is not clear whether this is followed in # practice. # https://tools.ietf.org/html/rfc6125#section-6.4.2 def normalize_cn(cn) cn.gsub("*", "wildcard").gsub(/[^[:alnum:]\-]/, '_') end def configuration Chef::Config end def trusted_certs_dir configuration.trusted_certs_dir end def write_cert(cert) FileUtils.mkdir_p(trusted_certs_dir) cn = cn_of(cert) filename = File.join(trusted_certs_dir, "#{normalize_cn(cn)}.crt") ui.msg("Adding certificate for #{cn} in #{filename}") File.open(filename, File::CREAT|File::TRUNC|File::RDWR, 0644) do |f| f.print(cert.to_s) end end def run validate_uri ui.warn(<<-TRUST_TRUST) Certificates from #{host} will be fetched and placed in your trusted_cert directory (#{trusted_certs_dir}). Knife has no means to verify these are the correct certificates. You should verify the authenticity of these certificates after downloading. TRUST_TRUST remote_cert_chain.each do |cert| write_cert(cert) end rescue OpenSSL::SSL::SSLError => e # 'unknown protocol' usually means you tried to connect to a non-ssl # service. We handle that specially here, any other error we let bubble # up (probably a bug of some sort). raise unless e.message.include?("unknown protocol") ui.error("The service at the given URI (#{uri}) does not accept SSL connections") if uri.scheme == "http" https_uri = uri.to_s.sub(/^http/, 'https') ui.error("Perhaps you meant to connect to '#{https_uri}'?") end exit 1 end end end end chef-12.3.0/lib/chef/knife/role_run_list_add.rb0000644000004100000410000000516012520074675021330 0ustar www-datawww-data# Author:: Adam Jacob () # Author:: William Albenzi () # 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 RoleRunListAdd < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role run_list add [ROLE] [ENTRY[,ENTRY]] (options)" option :after, :short => "-a ITEM", :long => "--after ITEM", :description => "Place the ENTRY in the run list after ITEM" def add_to_env_run_list(role, environment, entries, after=nil) if after nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end role.run_list_for(environment).each do |entry| nlist << entry if entry == after entries.each { |e| nlist << e } end end role.env_run_lists_add(environment => nlist) else nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end role.run_list_for(environment).each do |entry| nlist << entry end entries.each { |e| nlist << e } role.env_run_lists_add(environment => nlist) end end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = "_default" if @name_args.size > 1 # Check for nested lists and create a single plain one entries = @name_args[1..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[1].split(',').map { |e| e.strip } end add_to_env_run_list(role, environment, entries, config[:after]) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/client_show.rb0000644000004100000410000000236312520074675020160 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientShow < Knife include Knife::Core::MultiAttributeReturnOption deps do require 'chef/api_client' require 'chef/json_compat' end banner "knife client show CLIENT (options)" def run @client_name = @name_args[0] if @client_name.nil? show_usage ui.fatal("You must specify a client name") exit 1 end client = Chef::ApiClient.load(@client_name) output(format_for_display(client)) end end end end chef-12.3.0/lib/chef/knife/cookbook_show.rb0000644000004100000410000000701112520074675020503 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 CookbookShow < Knife deps do require 'chef/json_compat' require 'uri' require 'chef/cookbook_version' end banner "knife cookbook show COOKBOOK [VERSION] [PART] [FILENAME] (options)" option :fqdn, :short => "-f FQDN", :long => "--fqdn FQDN", :description => "The FQDN of the host to see the file for" option :platform, :short => "-p PLATFORM", :long => "--platform PLATFORM", :description => "The platform to see the file for" option :platform_version, :short => "-V VERSION", :long => "--platform-version VERSION", :description => "The platform version to see the file for" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run case @name_args.length when 4 # We are showing a specific file node = Hash.new node[:fqdn] = config[:fqdn] if config.has_key?(:fqdn) node[:platform] = config[:platform] if config.has_key?(:platform) node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version) class << node def attribute?(name) has_key?(name) end end cookbook_name, segment, filename = @name_args[0], @name_args[2], @name_args[3] cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1] cookbook = rest.get_rest("cookbooks/#{cookbook_name}/#{cookbook_version}") manifest_entry = cookbook.preferred_manifest_record(node, segment, filename) temp_file = rest.get_rest(manifest_entry[:url], true) # the temp file is cleaned up elsewhere temp_file.open if temp_file.closed? pretty_print(temp_file.read) when 3 # We are showing a specific part of the cookbook cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1] result = rest.get_rest("cookbooks/#{@name_args[0]}/#{cookbook_version}") output(result.manifest[@name_args[2]]) when 2 # We are showing the whole cookbook data cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1] output(rest.get_rest("cookbooks/#{@name_args[0]}/#{cookbook_version}")) when 1 # We are showing the cookbook versions (all of them) cookbook_name = @name_args[0] env = config[:environment] api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook_name}" : "cookbooks/#{cookbook_name}" output(format_cookbook_list_for_display(rest.get_rest(api_endpoint))) when 0 show_usage ui.fatal("You must specify a cookbook name") exit 1 end end end end end chef-12.3.0/lib/chef/knife/exec.rb0000644000004100000410000000523012520074675016562 0ustar www-datawww-data#-- # Author:: Daniel DeLeo ( "-E CODE", :long => "--exec CODE", :description => "a string of Chef code to execute" option :script_path, :short => "-p PATH:PATH", :long => "--script-path PATH:PATH", :description => "A colon-separated path to look for scripts in", :proc => lambda { |o| o.split(":") } deps do require 'chef/shell/ext' end def run config[:script_path] ||= Array(Chef::Config[:script_path]) # Default script paths are chef-repo/.chef/scripts and ~/.chef/scripts config[:script_path] << File.join(Chef::Knife.chef_config_dir, 'scripts') if Chef::Knife.chef_config_dir Chef::Util::PathHelper.home('.chef', 'scripts') { |p| config[:script_path] << p } scripts = Array(name_args) context = Object.new Shell::Extensions.extend_context_object(context) if config[:exec] context.instance_eval(config[:exec], "-E Argument", 0) elsif !scripts.empty? scripts.each do |script| file = find_script(script) context.instance_eval(IO.read(file), file, 0) end else script = STDIN.read context.instance_eval(script, "STDIN", 0) end end def find_script(x) # Try to find a script. First try expanding the path given. script = File.expand_path(x) return script if File.exists?(script) # Failing that, try searching the script path. If we can't find # anything, fail gracefully. Chef::Log.debug("Searching script_path: #{config[:script_path].inspect}") config[:script_path].each do |path| path = File.expand_path(path) test = File.join(path, x) Chef::Log.debug("Testing: #{test}") if File.exists?(test) script = test Chef::Log.debug("Found: #{test}") return script end end ui.error("\"#{x}\" not found in current directory or script_path, giving up.") exit(1) end end chef-12.3.0/lib/chef/knife/bootstrap/0000755000004100000410000000000012520074675017326 5ustar www-datawww-datachef-12.3.0/lib/chef/knife/bootstrap/chef_vault_handler.rb0000644000004100000410000001276712520074675023505 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 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. # class Chef class Knife class Bootstrap < Knife class ChefVaultHandler # @return [Hash] knife merged config, typically @config attr_accessor :knife_config # @return [Chef::Knife::UI] ui object for output attr_accessor :ui # @return [String] name of the node (technically name of the client) attr_reader :node_name # @param knife_config [Hash] knife merged config, typically @config # @param ui [Chef::Knife::UI] ui object for output def initialize(knife_config: {}, ui: nil) @knife_config = knife_config @ui = ui end # Updates the chef vault items for the newly created node. # # @param node_name [String] name of the node (technically name of the client) # @todo: node_name should be mandatory (ruby 2.0 compat) def run(node_name: nil) return unless doing_chef_vault? sanity_check @node_name = node_name ui.info("Updating Chef Vault, waiting for client to be searchable..") while wait_for_client update_bootstrap_vault_json! end # Iterate through all the vault items to update. Items may be either a String # or an Array of Strings: # # { # "vault1": "item", # "vault2": [ "item1", "item2", "item2" ] # } # def update_bootstrap_vault_json! vault_json.each do |vault, items| [ items ].flatten.each do |item| update_vault(vault, item) end end end # @return [Boolean] if we've got chef vault options to act on or not def doing_chef_vault? !!(bootstrap_vault_json || bootstrap_vault_file || bootstrap_vault_item) end private # warn if the user has given mutual conflicting options def sanity_check if bootstrap_vault_item && (bootstrap_vault_json || bootstrap_vault_file) ui.warn "--vault-item given with --vault-list or --vault-file, ignoring the latter" end if bootstrap_vault_json && bootstrap_vault_file ui.warn "--vault-list given with --vault-file, ignoring the latter" end end # @return [String] string with serialized JSON representing the chef vault items def bootstrap_vault_json knife_config[:bootstrap_vault_json] end # @return [String] JSON text in a file representing the chef vault items def bootstrap_vault_file knife_config[:bootstrap_vault_file] end # @return [Hash] Ruby object representing the chef vault items to create def bootstrap_vault_item knife_config[:bootstrap_vault_item] end # Helper to return a ruby object represeting all the data bags and items # to update via chef-vault. # # @return [Hash] deserialized ruby hash with all the vault items def vault_json @vault_json ||= begin if bootstrap_vault_item bootstrap_vault_item else json = bootstrap_vault_json ? bootstrap_vault_json : File.read(bootstrap_vault_file) Chef::JSONCompat.from_json(json) end end end # Update an individual vault item and save it # # @param vault [String] name of the chef-vault encrypted data bag # @param item [String] name of the chef-vault encrypted item def update_vault(vault, item) require_chef_vault! bootstrap_vault_item = load_chef_bootstrap_vault_item(vault, item) bootstrap_vault_item.clients("name:#{node_name}") bootstrap_vault_item.save end # Hook to stub out ChefVault # # @param vault [String] name of the chef-vault encrypted data bag # @param item [String] name of the chef-vault encrypted item # @returns [ChefVault::Item] ChefVault::Item object def load_chef_bootstrap_vault_item(vault, item) ChefVault::Item.load(vault, item) end public :load_chef_bootstrap_vault_item # for stubbing # Helper used to spin waiting for the client to appear in search. # # @return [Boolean] true if the client is searchable def wait_for_client sleep 1 !Chef::Search::Query.new.search(:client, "name:#{node_name}")[0] end # Helper to very lazily require the chef-vault gem def require_chef_vault! @require_chef_vault ||= begin require 'chef-vault' true rescue LoadError raise "Knife bootstrap cannot configure chef vault items when the chef-vault gem is not installed" end end end end end end chef-12.3.0/lib/chef/knife/bootstrap/templates/0000755000004100000410000000000012520074675021324 5ustar www-datawww-datachef-12.3.0/lib/chef/knife/bootstrap/templates/chef-aix.erb0000644000004100000410000000325112520074675023503 0ustar www-datawww-dataksh -c ' function exists { if type $1 >/dev/null 2>&1 then return 0 else return 1 fi } if ! exists /usr/bin/chef-client; then <% if @chef_config[:aix_package] -%> # Read the download URL/location from knife.rb with option aix_package rm -rf /tmp/chef_installer # ensure there no older pkg echo "<%= @chef_config[:aix_package] %>" perl -e '\''use LWP::Simple; getprint($ARGV[0]);'\'' <%= @chef_config[:aix_package] %> > /tmp/chef_installer installp -aYF -d /tmp/chef_installer chef <% else -%> echo ":aix_package location is not set in knife.rb" exit <% end -%> fi mkdir -p /etc/chef <% if client_pem -%> cat > /etc/chef/client.pem <<'EOP' <%= ::File.read(::File.expand_path(client_pem)) %> EOP chmod 0600 /etc/chef/client.pem <% end -%> <% if validation_key -%> cat > /etc/chef/validation.pem <<'EOP' <%= validation_key %> EOP chmod 0600 /etc/chef/validation.pem <% end -%> <% if encrypted_data_bag_secret -%> cat > /etc/chef/encrypted_data_bag_secret <<'EOP' <%= encrypted_data_bag_secret %> EOP chmod 0600 /etc/chef/encrypted_data_bag_secret <% end -%> <% unless trusted_certs.empty? -%> mkdir -p /etc/chef/trusted_certs <%= trusted_certs %> <% end -%> <%# Generate Ohai Hints -%> <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%> mkdir -p /etc/chef/ohai/hints <% @chef_config[:knife][:hints].each do |name, hash| -%> cat > /etc/chef/ohai/hints/<%= name %>.json <<'EOP' <%= Chef::JSONCompat.to_json(hash) %> EOP <% end -%> <% end -%> cat > /etc/chef/client.rb <<'EOP' <%= config_content %> EOP cat > /etc/chef/first-boot.json <<'EOP' <%= Chef::JSONCompat.to_json(first_boot) %> EOP <%= start_chef %>' chef-12.3.0/lib/chef/knife/bootstrap/templates/chef-full.erb0000644000004100000410000000446412520074675023673 0ustar www-datawww-databash -c ' <%= "export https_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%> distro=`uname -s` if test "x$distro" = "xSunOS"; then if test -d "/usr/sfw/bin"; then PATH=/usr/sfw/bin:$PATH export PATH fi fi exists() { if command -v $1 &>/dev/null then return 0 else return 1 fi } <% if knife_config[:bootstrap_install_command] %> <%= knife_config[:bootstrap_install_command] %> <% else %> install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://www.opscode.com/chef/install.sh" %>" if ! exists /usr/bin/chef-client; then echo "Installing Chef Client..." if exists wget; then bash <(wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %> <%= knife_config[:bootstrap_wget_options] %> ${install_sh} -O -) <%= latest_current_chef_version_string %> elif exists curl; then bash <(curl -L <%= "--proxy \"#{knife_config[:bootstrap_proxy]}\" " if knife_config[:bootstrap_proxy] %> <%= knife_config[:bootstrap_curl_options] %> ${install_sh}) <%= latest_current_chef_version_string %> else echo "Neither wget nor curl found. Please install one and try again." >&2 exit 1 fi fi <% end %> mkdir -p /etc/chef <% if client_pem -%> cat > /etc/chef/client.pem <<'EOP' <%= ::File.read(::File.expand_path(client_pem)) %> EOP chmod 0600 /etc/chef/client.pem <% end -%> <% if validation_key -%> cat > /etc/chef/validation.pem <<'EOP' <%= validation_key %> EOP chmod 0600 /etc/chef/validation.pem <% end -%> <% if encrypted_data_bag_secret -%> cat > /etc/chef/encrypted_data_bag_secret <<'EOP' <%= encrypted_data_bag_secret %> EOP chmod 0600 /etc/chef/encrypted_data_bag_secret <% end -%> <% unless trusted_certs.empty? -%> mkdir -p /etc/chef/trusted_certs <%= trusted_certs %> <% end -%> <%# Generate Ohai Hints -%> <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%> mkdir -p /etc/chef/ohai/hints <% @chef_config[:knife][:hints].each do |name, hash| -%> cat > /etc/chef/ohai/hints/<%= name %>.json <<'EOP' <%= Chef::JSONCompat.to_json(hash) %> EOP <% end -%> <% end -%> cat > /etc/chef/client.rb <<'EOP' <%= config_content %> EOP cat > /etc/chef/first-boot.json <<'EOP' <%= Chef::JSONCompat.to_json(first_boot) %> EOP echo "Starting first Chef Client run..." <%= start_chef %>' chef-12.3.0/lib/chef/knife/bootstrap/templates/archlinux-gems.erb0000644000004100000410000000420312520074675024743 0ustar www-datawww-databash -c ' <%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%> if [ ! -f /usr/bin/chef-client ]; then pacman -Syy pacman -S --noconfirm ruby ntp base-devel ntpdate -u pool.ntp.org gem install ohai --no-user-install --no-document --verbose gem install chef --no-user-install --no-document --verbose <%= Chef::VERSION %> fi mkdir -p /etc/chef <% if validation_key -%> cat > /etc/chef/validation.pem <<'EOP' <%= validation_key %> EOP chmod 0600 /etc/chef/validation.pem <% end -%> <% if encrypted_data_bag_secret -%> cat > /etc/chef/encrypted_data_bag_secret <<'EOP' <%= encrypted_data_bag_secret %> EOP chmod 0600 /etc/chef/encrypted_data_bag_secret <% end -%> <% unless trusted_certs.empty? -%> mkdir -p /etc/chef/trusted_certs <%= trusted_certs %> <% end -%> <%# Generate Ohai Hints -%> <% unless @chef_config[:knife][:hints].nil? || @chef_config[:knife][:hints].empty? -%> mkdir -p /etc/chef/ohai/hints <% @chef_config[:knife][:hints].each do |name, hash| -%> cat > /etc/chef/ohai/hints/<%= name %>.json <<'EOP' <%= Chef::JSONCompat.to_json(hash) %> EOP <% end -%> <% end -%> <% if client_pem -%> cat > /etc/chef/client.pem <<'EOP' <%= ::File.read(::File.expand_path(client_pem)) %> EOP chmod 0600 /etc/chef/client.pem <% end -%> cat > /etc/chef/client.rb <<'EOP' log_level :info log_location STDOUT chef_server_url "<%= @chef_config[:chef_server_url] %>" validation_client_name "<%= @chef_config[:validation_client_name] %>" <% if @config[:chef_node_name] -%> node_name "<%= @config[:chef_node_name] %>" <% else -%> # Using default node name (fqdn) <% end -%> # ArchLinux follows the Filesystem Hierarchy Standard file_cache_path "/var/cache/chef" file_backup_path "/var/lib/chef/backup" pid_file "/var/run/chef/client.pid" cache_options({ :path => "/var/cache/chef/checksums", :skip_expires => true}) <% if knife_config[:bootstrap_proxy] %> http_proxy "<%= knife_config[:bootstrap_proxy] %>" https_proxy "<%= knife_config[:bootstrap_proxy] %>" <% end -%> EOP cat > /etc/chef/first-boot.json <<'EOP' <%= Chef::JSONCompat.to_json(first_boot) %> EOP <%= start_chef %>' chef-12.3.0/lib/chef/knife/bootstrap/templates/README.md0000644000004100000410000000150212520074675022601 0ustar www-datawww-dataThis directory contains bootstrap templates which can be used with the -d flag to 'knife bootstrap' to install Chef in different ways. To simplify installation, and reduce the matrix of common installation patterns to support, we have standardized on the [Omnibus](https://github.com/opscode/omnibus-ruby) built installation packages. The 'chef-full' template downloads a script which is used to determine the correct Omnibus package for this system from the [Omnitruck](https://github.com/opscode/opscode-omnitruck) API. All other templates in this directory are deprecated and will be removed in the future. You can still utilize custom bootstrap templates on your system if your installation needs are unique. Additional information can be found on the [docs site](http://docs.opscode.com/knife_bootstrap.html#custom-templates).chef-12.3.0/lib/chef/knife/bootstrap/client_builder.rb0000644000004100000410000001502112520074675022636 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 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/node' require 'chef/rest' require 'chef/api_client/registration' require 'chef/api_client' require 'tmpdir' class Chef class Knife class Bootstrap < Knife class ClientBuilder # @return [Hash] knife merged config, typically @config attr_accessor :knife_config # @return [Hash] chef config object attr_accessor :chef_config # @return [Chef::Knife::UI] ui object for output attr_accessor :ui # @param knife_config [Hash] Hash of knife config settings # @param chef_config [Hash] Hash of chef config settings # @param ui [Chef::Knife::UI] UI object for output def initialize(knife_config: {}, chef_config: {}, ui: nil) @knife_config = knife_config @chef_config = chef_config @ui = ui end # Main entry. Prompt the user to clean up any old client or node objects. Then create # the new client, then create the new node. def run sanity_check ui.info("Creating new client for #{node_name}") create_client! ui.info("Creating new node for #{node_name}") create_node! end # Tempfile to use to write newly created client credentials to. # # This method is public so that the knife bootstrapper can read then and pass the value into # the handler for chef vault which needs the client cert we create here. # # We hang onto the tmpdir as an ivar as well so that it will not get GC'd and removed # # @return [String] path to the generated client.pem def client_path @client_path ||= begin @tmpdir = Dir.mktmpdir File.join(@tmpdir, "#{node_name}.pem") end end private # @return [String] node name from the knife_config def node_name knife_config[:chef_node_name] end # @return [String] enviroment from the knife_config def environment knife_config[:environment] end # @return [String] run_list from the knife_config def run_list knife_config[:run_list] end # @return [Hash,Array] Object representation of json first-boot attributes from the knife_config def first_boot_attributes knife_config[:first_boot_attributes] end # @return [String] chef server url from the Chef::Config def chef_server_url chef_config[:chef_server_url] end # Accesses the run_list and coerces it into an Array, changing nils into # the empty Array, and splitting strings representations of run_lists into # Arrays. # # @return [Array] run_list coerced into an array def normalized_run_list case run_list when nil [] when String run_list.split(/\s*,\s*/) when Array run_list end end # Create the client object and save it to the Chef API def create_client! Chef::ApiClient::Registration.new(node_name, client_path, http_api: rest).run end # Create the node object (via the lazy accessor) and save it to the Chef API def create_node! node.save end # Create a new Chef::Node. Supports creating the node with its name, run_list, attributes # and environment. This injects a rest object into the Chef::Node which uses the client key # for authentication so that the client creates the node and therefore we get the acls setup # correctly. # # @return [Chef::Node] new chef node to create def node @node ||= begin node = Chef::Node.new(chef_server_rest: client_rest) node.name(node_name) node.run_list(normalized_run_list) node.normal_attrs = first_boot_attributes if first_boot_attributes node.environment(environment) if environment node end end # Check for the existence of a node and/or client already on the server. If the node # already exists, we must delete it in order to proceed so that we can create a new node # object with the permissions of the new client. There is a use case for creating a new # client and wiring it up to a precreated node object, but we do currently support that. # # We prompt the user about what to do and will fail hard if we do not get confirmation to # delete any prior node/client objects. def sanity_check if resource_exists?("nodes/#{node_name}") ui.confirm("Node #{node_name} exists, overwrite it") rest.delete("nodes/#{node_name}") end if resource_exists?("clients/#{node_name}") ui.confirm("Client #{node_name} exists, overwrite it") rest.delete("clients/#{node_name}") end end # Check if an relative path exists on the chef server # # @param relative_path [String] URI path relative to the chef organization # @return [Boolean] if the relative path exists or returns a 404 def resource_exists?(relative_path) rest.get_rest(relative_path) true rescue Net::HTTPServerException => e raise unless e.response.code == "404" false end # @return [Chef::REST] REST client using the client credentials def client_rest @client_rest ||= Chef::REST.new(chef_server_url, node_name, client_path) end # @return [Chef::REST] REST client using the cli user's knife credentials # this uses the users's credentials def rest @rest ||= Chef::REST.new(chef_server_url) end end end end end chef-12.3.0/lib/chef/knife/node_environment_set.rb0000644000004100000410000000253712520074675022071 0ustar www-datawww-data# # Author:: Jimmy McCrory () # Copyright:: Copyright (c) 2014 Jimmy McCrory # 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 NodeEnvironmentSet < Knife deps do require 'chef/node' end banner "knife node environment set NODE ENVIRONMENT" def run if @name_args.size < 2 ui.fatal "You must specify a node name and an environment." show_usage exit 1 else @node_name = @name_args[0] @environment = @name_args[1] end node = Chef::Node.load(@node_name) node.chef_environment = @environment node.save config[:attribute] = "chef_environment" output(format_for_display(node)) end end end end chef-12.3.0/lib/chef/knife/configure.rb0000644000004100000410000001546612520074675017633 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 Configure < Knife attr_reader :chef_server, :new_client_name, :admin_client_name, :admin_client_key attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key deps do require 'ohai' Chef::Knife::ClientCreate.load_deps Chef::Knife::UserCreate.load_deps end banner "knife configure (options)" option :repository, :short => "-r REPO", :long => "--repository REPO", :description => "The path to the chef-repo" option :initial, :short => "-i", :long => "--initial", :boolean => true, :description => "Use to create a API client, typically an administrator client on a freshly-installed server" option :admin_client_name, :long => "--admin-client-name NAME", :description => "The name of the client, typically the name of the admin client" option :admin_client_key, :long => "--admin-client-key PATH", :description => "The path to the private key used by the client, typically a file named admin.pem" option :validation_client_name, :long => "--validation-client-name NAME", :description => "The name of the validation client, typically a client named chef-validator" option :validation_key, :long => "--validation-key PATH", :description => "The path to the validation key used by the client, typically a file named validation.pem" def configure_chef # We are just faking out the system so that you can do this without a key specified Chef::Config[:node_name] = 'woot' super Chef::Config[:node_name] = nil end def run ask_user_for_config_path FileUtils.mkdir_p(chef_config_path) ask_user_for_config ::File.open(config[:config_file], "w") do |f| f.puts <<-EOH log_level :info log_location STDOUT node_name '#{new_client_name}' client_key '#{new_client_key}' validation_client_name '#{validation_client_name}' validation_key '#{validation_key}' chef_server_url '#{chef_server}' syntax_check_cache_path '#{File.join(chef_config_path, "syntax_check_cache")}' EOH unless chef_repo.empty? f.puts "cookbook_path [ '#{chef_repo}/cookbooks' ]" end end if config[:initial] ui.msg("Creating initial API user...") Chef::Config[:chef_server_url] = chef_server Chef::Config[:node_name] = admin_client_name Chef::Config[:client_key] = admin_client_key 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("") ui.msg("You must place your client key in:") ui.msg(" #{new_client_key}") ui.msg("Before running commands with Knife!") ui.msg("") ui.msg("*****") ui.msg("") ui.msg("You must place your validation key in:") ui.msg(" #{validation_key}") ui.msg("Before generating instance data with Knife!") ui.msg("") ui.msg("*****") end ui.msg("Configuration file written to #{config[:config_file]}") end def ask_user_for_config_path config[:config_file] ||= ask_question("Where should I put the config file? ", :default => "#{Chef::Config[:user_home]}/.chef/knife.rb") # have to use expand path to expand the tilde character to the user's home config[:config_file] = File.expand_path(config[:config_file]) if File.exists?(config[:config_file]) confirm("Overwrite #{config[:config_file]}") end end def ask_user_for_config server_name = guess_servername @chef_server = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "https://#{server_name}:443") if config[:initial] @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-server/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) end @validation_client_name = config[:validation_client_name] || ask_question("Please enter the validation clientname: ", :default => 'chef-validator') @validation_key = config[:validation_key] || ask_question("Please enter the location of the validation key: ", :default => '/etc/chef-server/chef-validator.pem') @validation_key = File.expand_path(@validation_key) @chef_repo = config[:repository] || ask_question("Please enter the path to a chef repository (or leave blank): ") @new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem") @new_client_key = File.expand_path(@new_client_key) end def guess_servername o = Ohai::System.new o.load_plugins o.require_plugin 'os' o.require_plugin 'hostname' o[:fqdn] || o[:machinename] || o[:hostname] || 'localhost' end def config_file config[:config_file] end def chef_config_path File.dirname(config_file) end end end end chef-12.3.0/lib/chef/knife/node_show.rb0000644000004100000410000000337112520074675017627 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/knife/core/node_presenter' class Chef class Knife class NodeShow < Knife include Knife::Core::NodeFormattingOptions include Knife::Core::MultiAttributeReturnOption deps do require 'chef/node' require 'chef/json_compat' end banner "knife node show NODE (options)" option :run_list, :short => "-r", :long => "--run-list", :description => "Show only the run list" option :environment, :short => "-E", :long => "--environment", :description => "Show only the Chef environment" def run ui.use_presenter Knife::Core::NodePresenter @node_name = @name_args[0] if @node_name.nil? show_usage ui.fatal("You must specify a node name") exit 1 end node = Chef::Node.load(@node_name) output(format_for_display(node)) self.class.attrs_to_show = [] end def self.attrs_to_show=(attrs) @attrs_to_show = attrs end end end end chef-12.3.0/lib/chef/knife/cookbook_create.rb0000644000004100000410000003477112520074675021003 0ustar www-datawww-data# # Author:: Nuo Yan () # 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 CookbookCreate < Knife deps do require 'chef/json_compat' require 'uri' require 'fileutils' end banner "knife cookbook create COOKBOOK (options)" option :cookbook_path, :short => "-o PATH", :long => "--cookbook-path PATH", :description => "The directory where the cookbook will be created" option :readme_format, :short => "-r FORMAT", :long => "--readme-format FORMAT", :description => "Format of the README file, supported formats are 'md' (markdown) and 'rdoc' (rdoc)" option :cookbook_license, :short => "-I LICENSE", :long => "--license LICENSE", :description => "License for cookbook, apachev2, gplv2, gplv3, mit or none" option :cookbook_copyright, :short => "-C COPYRIGHT", :long => "--copyright COPYRIGHT", :description => "Name of Copyright holder" option :cookbook_email, :short => "-m EMAIL", :long => "--email EMAIL", :description => "Email address of cookbook maintainer" def run self.config = Chef::Config.merge!(config) if @name_args.length < 1 show_usage ui.fatal("You must specify a cookbook name") exit 1 end if default_cookbook_path_empty? && parameter_empty?(config[:cookbook_path]) raise ArgumentError, "Default cookbook_path is not specified in the knife.rb config file, and a value to -o is not provided. Nowhere to write the new cookbook to." end cookbook_path = File.expand_path(Array(config[:cookbook_path]).first) cookbook_name = @name_args.first copyright = config[:cookbook_copyright] || "YOUR_COMPANY_NAME" email = config[:cookbook_email] || "YOUR_EMAIL" license = ((config[:cookbook_license] != "false") && config[:cookbook_license]) || "none" readme_format = ((config[:readme_format] != "false") && config[:readme_format]) || "md" create_cookbook(cookbook_path, cookbook_name, copyright, license) create_readme(cookbook_path, cookbook_name, readme_format) create_changelog(cookbook_path, cookbook_name) create_metadata(cookbook_path, cookbook_name, copyright, email, license, readme_format) end def create_cookbook(dir, cookbook_name, copyright, license) msg("** Creating cookbook #{cookbook_name} in #{dir}") FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "attributes")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "recipes")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "definitions")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "libraries")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "resources")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "providers")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "files", "default")}" FileUtils.mkdir_p "#{File.join(dir, cookbook_name, "templates", "default")}" unless File.exists?(File.join(dir, cookbook_name, "recipes", "default.rb")) open(File.join(dir, cookbook_name, "recipes", "default.rb"), "w") do |file| file.puts <<-EOH # # Cookbook Name:: #{cookbook_name} # Recipe:: default # # Copyright #{Time.now.year}, #{copyright} # EOH case license when "apachev2" file.puts <<-EOH # 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. # EOH when "gplv2" file.puts <<-EOH # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # EOH when "gplv3" file.puts <<-EOH # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # EOH when "mit" file.puts <<-EOH # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # EOH when "none" file.puts <<-EOH # All rights reserved - Do Not Redistribute # EOH end end end end def create_changelog(dir, cookbook_name) msg("** Creating CHANGELOG for cookbook: #{cookbook_name}") unless File.exists?(File.join(dir,cookbook_name,'CHANGELOG.md')) open(File.join(dir, cookbook_name, 'CHANGELOG.md'),'w') do |file| file.puts <<-EOH #{cookbook_name} CHANGELOG #{'='*"#{cookbook_name} CHANGELOG".length} This file is used to list changes made in each version of the #{cookbook_name} cookbook. 0.1.0 ----- - [your_name] - Initial release of #{cookbook_name} - - - Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. EOH end end end def create_readme(dir, cookbook_name, readme_format) msg("** Creating README for cookbook: #{cookbook_name}") unless File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}")) open(File.join(dir, cookbook_name, "README.#{readme_format}"), "w") do |file| case readme_format when "rdoc" file.puts <<-EOH = #{cookbook_name} Cookbook TODO: Enter the cookbook description here. e.g. This cookbook makes your favorite breakfast sandwich. == Requirements TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. e.g. ==== packages - +toaster+ - #{cookbook_name} needs toaster to brown your bagel. == Attributes TODO: List your cookbook attributes here. e.g. ==== #{cookbook_name}::default
Key Type Description Default
['#{cookbook_name}']['bacon'] Boolean whether to include bacon true
== Usage ==== #{cookbook_name}::default TODO: Write usage instructions for each cookbook. e.g. Just include +#{cookbook_name}+ in your node's +run_list+: { "name":"my_node", "run_list": [ "recipe[#{cookbook_name}]" ] } == Contributing TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. e.g. 1. Fork the repository on Github 2. Create a named feature branch (like `add_component_x`) 3. Write your change 4. Write tests for your change (if applicable) 5. Run the tests, ensuring they all pass 6. Submit a Pull Request using Github == License and Authors Authors: TODO: List authors EOH when "md","mkd","txt" file.puts <<-EOH #{cookbook_name} Cookbook #{'='*"#{cookbook_name} Cookbook".length} TODO: Enter the cookbook description here. e.g. This cookbook makes your favorite breakfast sandwich. Requirements ------------ TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. e.g. #### packages - `toaster` - #{cookbook_name} needs toaster to brown your bagel. Attributes ---------- TODO: List your cookbook attributes here. e.g. #### #{cookbook_name}::default
Key Type Description Default
['#{cookbook_name}']['bacon'] Boolean whether to include bacon true
Usage ----- #### #{cookbook_name}::default TODO: Write usage instructions for each cookbook. e.g. Just include `#{cookbook_name}` in your node's `run_list`: ```json { "name":"my_node", "run_list": [ "recipe[#{cookbook_name}]" ] } ``` Contributing ------------ TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. e.g. 1. Fork the repository on Github 2. Create a named feature branch (like `add_component_x`) 3. Write your change 4. Write tests for your change (if applicable) 5. Run the tests, ensuring they all pass 6. Submit a Pull Request using Github License and Authors ------------------- Authors: TODO: List authors EOH else file.puts <<-EOH #{cookbook_name} Cookbook #{'='*"#{cookbook_name} Cookbook".length} TODO: Enter the cookbook description here. e.g. This cookbook makes your favorite breakfast sandwich. Requirements TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. e.g. toaster #{cookbook_name} needs toaster to brown your bagel. Attributes TODO: List your cookbook attributes here. #{cookbook_name} Key Type Description Default ['#{cookbook_name}']['bacon'] Boolean whether to include bacon true Usage #{cookbook_name} TODO: Write usage instructions for each cookbook. e.g. Just include `#{cookbook_name}` in your node's `run_list`: [code] { "name":"my_node", "run_list": [ "recipe[#{cookbook_name}]" ] } [/code] Contributing TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. e.g. 1. Fork the repository on Github 2. Create a named feature branch (like `add_component_x`) 3. Write your change 4. Write tests for your change (if applicable) 5. Run the tests, ensuring they all pass 6. Submit a Pull Request using Github License and Authors Authors: TODO: List authors EOH end end end end def create_metadata(dir, cookbook_name, copyright, email, license, readme_format) msg("** Creating metadata for cookbook: #{cookbook_name}") license_name = case license when "apachev2" "Apache 2.0" when "gplv2" "GNU Public License 2.0" when "gplv3" "GNU Public License 3.0" when "mit" "MIT" when "none" "All rights reserved" end unless File.exists?(File.join(dir, cookbook_name, "metadata.rb")) open(File.join(dir, cookbook_name, "metadata.rb"), "w") do |file| if File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}")) long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.#{readme_format}'))" end file.puts <<-EOH name '#{cookbook_name}' maintainer '#{copyright}' maintainer_email '#{email}' license '#{license_name}' description 'Installs/Configures #{cookbook_name}' #{long_description} version '0.1.0' EOH end end end private def default_cookbook_path_empty? Chef::Config[:cookbook_path].nil? || Chef::Config[:cookbook_path].empty? end def parameter_empty?(parameter) parameter.nil? || parameter.empty? end end end end chef-12.3.0/lib/chef/knife/environment_list.rb0000644000004100000410000000217612520074675021243 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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 EnvironmentList < Knife deps do require 'chef/environment' require 'chef/json_compat' end banner "knife environment list (options)" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run output(format_list_for_display(Chef::Environment.list)) end end end end chef-12.3.0/lib/chef/knife/node_create.rb0000644000004100000410000000225112520074675020106 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeCreate < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node create NODE (options)" def run @node_name = @name_args[0] if @node_name.nil? show_usage ui.fatal("You must specify a node name") exit 1 end node = Chef::Node.new node.name(@node_name) create_object(node) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_install.rb0000644000004100000410000001403212520074675022216 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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' require 'chef/exceptions' require 'shellwords' class Chef class Knife class CookbookSiteInstall < Knife deps do require 'chef/mixin/shell_out' require 'chef/knife/core/cookbook_scm_repo' require 'chef/cookbook/metadata' end banner "knife cookbook site install COOKBOOK [VERSION] (options)" category "cookbook site" option :no_deps, :short => "-D", :long => "--skip-dependencies", :boolean => true, :default => false, :description => "Skips automatic dependency installation." option :cookbook_path, :short => "-o PATH:PATH", :long => "--cookbook-path PATH:PATH", :description => "A colon-separated path to look for cookbooks in", :proc => lambda { |o| o.split(":") } option :default_branch, :short => "-B BRANCH", :long => "--branch BRANCH", :description => "Default branch to work with", :default => "master" option :use_current_branch, :short => "-b", :long => "--use-current-branch", :description => "Use the current branch", :boolean => true, :default => false attr_reader :cookbook_name attr_reader :vendor_path def run extend Chef::Mixin::ShellOut if config[:cookbook_path] Chef::Config[:cookbook_path] = config[:cookbook_path] else config[:cookbook_path] = Chef::Config[:cookbook_path] end @cookbook_name = parse_name_args! # Check to ensure we have a valid source of cookbooks before continuing # @install_path = File.expand_path(Array(config[:cookbook_path]).first) ui.info "Installing #@cookbook_name to #{@install_path}" @repo = CookbookSCMRepo.new(@install_path, ui, config) #cookbook_path = File.join(vendor_path, name_args[0]) upstream_file = File.join(@install_path, "#{@cookbook_name}.tar.gz") @repo.sanity_check unless config[:use_current_branch] @repo.reset_to_default_state @repo.prepare_to_import(@cookbook_name) end downloader = download_cookbook_to(upstream_file) clear_existing_files(File.join(@install_path, @cookbook_name)) extract_cookbook(upstream_file, downloader.version) # TODO: it'd be better to store these outside the cookbook repo and # keep them around, e.g., in ~/Library/Caches on OS X. ui.info("removing downloaded tarball") File.unlink(upstream_file) if @repo.finalize_updates_to(@cookbook_name, downloader.version) unless config[:use_current_branch] @repo.reset_to_default_state end @repo.merge_updates_from(@cookbook_name, downloader.version) else unless config[:use_current_branch] @repo.reset_to_default_state end end unless config[:no_deps] preferred_metadata.dependencies.each do |cookbook, version_list| # Doesn't do versions.. yet nv = self.class.new nv.config = config nv.name_args = [ cookbook ] nv.run end end end def parse_name_args! if name_args.empty? ui.error("Please specify a cookbook to download and install.") exit 1 elsif name_args.size >= 2 unless name_args.last.match(/^(\d+)(\.\d+){1,2}$/) and name_args.size == 2 ui.error("Installing multiple cookbooks at once is not supported.") exit 1 end end name_args.first end def download_cookbook_to(download_path) downloader = Chef::Knife::CookbookSiteDownload.new downloader.config[:file] = download_path downloader.name_args = name_args downloader.run downloader end def extract_cookbook(upstream_file, version) ui.info("Uncompressing #{@cookbook_name} version #{version}.") # FIXME: Detect if we have the bad tar from git on Windows: https://github.com/opscode/chef/issues/1753 shell_out!("tar zxvf #{convert_path upstream_file}", :cwd => @install_path) end def clear_existing_files(cookbook_path) ui.info("Removing pre-existing version.") FileUtils.rmtree(cookbook_path) if File.directory?(cookbook_path) end def convert_path(upstream_file) # converts a Windows path (C:\foo) to a mingw path (/c/foo) if ENV['MSYSTEM'] == 'MINGW32' return upstream_file.sub(/^([[:alpha:]]):/, '/\1') else return Shellwords.escape upstream_file end end # Get the preferred metadata path on disk. Chef prefers the metadata.rb # over the metadata.json. # # @raise if there is no metadata in the cookbook # # @return [Chef::Cookbook::Metadata] def preferred_metadata md = Chef::Cookbook::Metadata.new rb = File.join(@install_path, @cookbook_name, "metadata.rb") if File.exist?(rb) md.from_file(rb) return md end json = File.join(@install_path, @cookbook_name, "metadata.json") if File.exist?(json) json = IO.read(json) md.from_json(json) return md end raise Chef::Exceptions::MetadataNotFound.new(@install_path, @cookbook_name) end end end end chef-12.3.0/lib/chef/knife/data_bag_from_file.rb0000644000004100000410000000643012520074675021405 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2010 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' require 'chef/util/path_helper' require 'chef/knife/data_bag_secret_options' class Chef class Knife class DataBagFromFile < Knife include DataBagSecretOptions deps do require 'chef/data_bag' require 'chef/data_bag_item' require 'chef/knife/core/object_loader' require 'chef/json_compat' require 'chef/encrypted_data_bag_item' end banner "knife data bag from file BAG FILE|FOLDER [FILE|FOLDER..] (options)" category "data bag" option :all, :short => "-a", :long => "--all", :description => "Upload all data bags or all items for specified data bags" def loader @loader ||= Knife::Core::ObjectLoader.new(DataBagItem, ui) end def run if config[:all] == true load_all_data_bags(@name_args) else if @name_args.size < 2 ui.msg(opt_parser) exit(1) end @data_bag = @name_args.shift load_data_bag_items(@data_bag, @name_args) end end private def data_bags_path @data_bag_path ||= "data_bags" end def find_all_data_bags loader.find_all_object_dirs("./#{data_bags_path}") end def find_all_data_bag_items(data_bag) loader.find_all_objects("./#{data_bags_path}/#{data_bag}") end def load_all_data_bags(args) data_bags = args.empty? ? find_all_data_bags : [args.shift] data_bags.each do |data_bag| load_data_bag_items(data_bag) end end def load_data_bag_items(data_bag, items = nil) items ||= find_all_data_bag_items(data_bag) item_paths = normalize_item_paths(items) item_paths.each do |item_path| item = loader.load_from("#{data_bags_path}", data_bag, item_path) item = if encryption_secret_provided? Chef::EncryptedDataBagItem.encrypt_data_bag_item(item, read_secret) else item end dbag = Chef::DataBagItem.new dbag.data_bag(data_bag) dbag.raw_data = item dbag.save ui.info("Updated data_bag_item[#{dbag.data_bag}::#{dbag.id}]") end end def normalize_item_paths(args) paths = Array.new args.each do |path| if File.directory?(path) paths.concat(Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "*.json"))) else paths << path end end paths end end end end chef-12.3.0/lib/chef/knife/environment_edit.rb0000644000004100000410000000224212520074675021207 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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 EnvironmentEdit < Knife deps do require 'chef/environment' require 'chef/json_compat' end banner "knife environment edit ENVIRONMENT (options)" def run env_name = @name_args[0] if env_name.nil? show_usage ui.fatal("You must specify an environment name") exit 1 end edit_object(Chef::Environment, env_name) end end end end chef-12.3.0/lib/chef/knife/node_edit.rb0000644000004100000410000000341612520074675017574 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeEdit < Knife deps do require 'chef/node' require 'chef/json_compat' require 'chef/knife/core/node_editor' end banner "knife node edit NODE (options)" option :all_attributes, :short => "-a", :long => "--all", :boolean => true, :description => "Display all attributes when editing" def run if node_name.nil? show_usage ui.fatal("You must specify a node name") exit 1 end updated_node = node_editor.edit_node if updated_values = node_editor.updated? ui.info "Saving updated #{updated_values.join(', ')} on node #{node.name}" updated_node.save else ui.info "Node not updated, skipping node save" end end def node_name @node_name ||= @name_args[0] end def node_editor @node_editor ||= Knife::NodeEditor.new(node, ui, config) end def node @node ||= Chef::Node.load(node_name) end end end end chef-12.3.0/lib/chef/knife/client_bulk_delete.rb0000644000004100000410000000620712520074675021460 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientBulkDelete < Knife deps do require 'chef/api_client' require 'chef/json_compat' end option :delete_validators, :short => "-D", :long => "--delete-validators", :description => "Force deletion of clients if they're validators" banner "knife client bulk delete REGEX (options)" def run if name_args.length < 1 ui.fatal("You must supply a regular expression to match the results against") exit 42 end all_clients = Chef::ApiClient.list(true) matcher = /#{name_args[0]}/ clients_to_delete = {} validators_to_delete = {} all_clients.each do |name, client| next unless name =~ matcher if client.validator validators_to_delete[client.name] = client else clients_to_delete[client.name] = client end end if clients_to_delete.empty? && validators_to_delete.empty? ui.info "No clients match the expression /#{name_args[0]}/" exit 0 end check_and_delete_validators(validators_to_delete) check_and_delete_clients(clients_to_delete) end def check_and_delete_validators(validators) unless validators.empty? unless config[:delete_validators] ui.msg("Following clients are validators and will not be deleted.") print_clients(validators) ui.msg("You must specify --delete-validators to delete the validator clients") else ui.msg("The following validators will be deleted:") print_clients(validators) if ui.confirm_without_exit("Are you sure you want to delete these validators") destroy_clients(validators) end end end end def check_and_delete_clients(clients) unless clients.empty? ui.msg("The following clients will be deleted:") print_clients(clients) ui.confirm("Are you sure you want to delete these clients") destroy_clients(clients) end end def destroy_clients(clients) clients.sort.each do |name, client| client.destroy ui.msg("Deleted client #{name}") end end def print_clients(clients) ui.msg("") ui.msg(ui.list(clients.keys.sort, :columns_down)) ui.msg("") end end end end chef-12.3.0/lib/chef/knife/data_bag_edit.rb0000644000004100000410000000462612520074675020375 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2009-2010 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' require 'chef/knife/data_bag_secret_options' class Chef class Knife class DataBagEdit < Knife include DataBagSecretOptions deps do require 'chef/data_bag_item' require 'chef/encrypted_data_bag_item' end banner "knife data bag edit BAG ITEM (options)" category "data bag" def load_item(bag, item_name) item = Chef::DataBagItem.load(bag, item_name) if encrypted?(item.raw_data) if encryption_secret_provided_ignore_encrypt_flag? return Chef::EncryptedDataBagItem.new(item, read_secret).to_hash, true else ui.fatal("You cannot edit an encrypted data bag without providing the secret.") exit(1) end else return item, false end end def run if @name_args.length != 2 stdout.puts "You must supply the data bag and an item to edit!" stdout.puts opt_parser exit 1 end item, was_encrypted = load_item(@name_args[0], @name_args[1]) edited_item = edit_data(item) if was_encrypted || encryption_secret_provided? ui.info("Encrypting data bag using provided secret.") item_to_save = Chef::EncryptedDataBagItem.encrypt_data_bag_item(edited_item, read_secret) else ui.info("Saving data bag unencrypted. To encrypt it, provide an appropriate secret.") item_to_save = edited_item end rest.put_rest("data/#{@name_args[0]}/#{@name_args[1]}", item_to_save) stdout.puts("Saved data_bag_item[#{@name_args[1]}]") ui.output(edited_item) if config[:print_after] end end end end chef-12.3.0/lib/chef/knife/node_run_list_set.rb0000644000004100000410000000353212520074675021360 0ustar www-datawww-data# # Author:: Mike Fiedler () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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 NodeRunListSet < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node run_list set NODE ENTRIES (options)" def run if @name_args.size < 2 ui.fatal "You must supply both a node name and a run list." show_usage exit 1 elsif @name_args.size > 2 # Check for nested lists and create a single plain one entries = @name_args[1..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[1].split(',').map { |e| e.strip } end node = Chef::Node.load(@name_args[0]) set_run_list(node, entries) node.save config[:run_list] = true output(format_for_display(node)) end # Clears out any existing run_list_items and sets them to the # specified entries def set_run_list(node, entries) node.run_list.run_list_items.clear entries.each { |e| node.run_list << e } end end end end chef-12.3.0/lib/chef/knife/upload.rb0000644000004100000410000000373612520074675017133 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Upload < Chef::ChefFS::Knife banner "knife upload PATTERNS" category "path-based" deps do require 'chef/chef_fs/command_line' end option :recurse, :long => '--[no-]recurse', :boolean => true, :default => true, :description => "List directories recursively." option :purge, :long => '--[no-]purge', :boolean => true, :default => false, :description => "Delete matching local files and directories that do not exist remotely." option :force, :long => '--[no-]force', :boolean => true, :default => false, :description => "Force upload of files even if they match (quicker for many files). Will overwrite frozen cookbooks." option :freeze, :long => '--[no-]freeze', :boolean => true, :default => false, :description => "Freeze cookbooks that get uploaded." option :dry_run, :long => '--dry-run', :short => '-n', :boolean => true, :default => false, :description => "Don't take action, only print what would happen" option :diff, :long => '--[no-]diff', :boolean => true, :default => true, :description => 'Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff' def run if name_args.length == 0 show_usage ui.fatal("Must specify at least one argument. If you want to upload everything in this directory, type \"knife upload .\"") exit 1 end error = false pattern_args.each do |pattern| if Chef::ChefFS::FileSystem.copy_to(pattern, local_fs, chef_fs, config[:recurse] ? nil : 1, config, ui, proc { |entry| format_path(entry) }) error = true end end if error exit 1 end end end end end chef-12.3.0/lib/chef/knife/tag_delete.rb0000644000004100000410000000331112520074675017731 0ustar www-datawww-data# # Author:: Ryan Davis () # Author:: Daniel DeLeo () # Author:: Nuo Yan () # Copyright:: Copyright (c) 2011 Ryan Davis and 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 TagDelete < Knife deps do require 'chef/node' end banner "knife tag delete NODE TAG ..." def run name = @name_args[0] tags = @name_args[1..-1] if name.nil? || tags.nil? || tags.empty? show_usage ui.fatal("You must specify a node name and at least one tag.") exit 1 end node = Chef::Node.load name deleted_tags = Array.new tags.each do |tag| unless node.tags.delete(tag).nil? deleted_tags << tag end end node.save message = if deleted_tags.empty? "Nothing has changed. The tags requested to be deleted do not exist." else "Deleted tags #{deleted_tags.join(", ")} for node #{name}." end ui.info(message) end end end end chef-12.3.0/lib/chef/knife/node_run_list_add.rb0000644000004100000410000000551412520074675021317 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeRunListAdd < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node run_list add [NODE] [ENTRY[,ENTRY]] (options)" option :after, :short => "-a ITEM", :long => "--after ITEM", :description => "Place the ENTRY in the run list after ITEM" option :before, :short => "-b ITEM", :long => "--before ITEM", :description => "Place the ENTRY in the run list before ITEM" def run node = Chef::Node.load(@name_args[0]) if @name_args.size > 2 # Check for nested lists and create a single plain one entries = @name_args[1..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[1].split(',').map { |e| e.strip } end if config[:after] && config[:before] ui.fatal("You cannot specify both --before and --after!") exit 1 end if config[:after] add_to_run_list_after(node, entries, config[:after]) elsif config[:before] add_to_run_list_before(node, entries, config[:before]) else add_to_run_list_after(node, entries) end node.save config[:run_list] = true output(format_for_display(node)) end private def add_to_run_list_after(node, entries, after=nil) if after nlist = [] node.run_list.each do |entry| nlist << entry if entry == after entries.each { |e| nlist << e } end end node.run_list.reset!(nlist) else entries.each { |e| node.run_list << e } end end def add_to_run_list_before(node, entries, before) nlist = [] node.run_list.each do |entry| if entry == before entries.each { |e| nlist << e } end nlist << entry end node.run_list.reset!(nlist) end end end end chef-12.3.0/lib/chef/knife/node_list.rb0000644000004100000410000000226512520074675017623 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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 NodeList < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node list (options)" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run env = Chef::Config[:environment] output(format_list_for_display( env ? Chef::Node.list_by_environment(env) : Chef::Node.list )) end end end end chef-12.3.0/lib/chef/knife/environment_compare.rb0000644000004100000410000000735712520074675021724 0ustar www-datawww-data# # Author:: Sander Botman () # Copyright:: Copyright (c) 2013 Sander Botman. # 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 EnvironmentCompare < Knife deps do require 'chef/environment' end banner "knife environment compare [ENVIRONMENT..] (options)" option :all, :short => "-a", :long => "--all", :description => "Show all cookbooks", :boolean => true option :mismatch, :short => "-m", :long => "--mismatch", :description => "Only show mismatching versions", :boolean => true def run # Get the commandline environments or all if none are provided. environments = environment_list # Get a list of all cookbooks that have constraints and their environment. constraints = constraint_list(environments) # Get the total list of cookbooks that have constraints cookbooks = cookbook_list(constraints) # If we cannot find any cookbooks, we can stop here. if cookbooks.nil? || cookbooks.empty? ui.error "Cannot find any environment cookbook constraints" exit 1 end # Get all cookbooks so we can compare them all cookbooks = rest.get_rest("/cookbooks?num_versions=1") if config[:all] # display matrix view of in the requested format. if config[:format] == 'summary' matrix = matrix_output(cookbooks, constraints) ui.output(matrix) else ui.output(constraints) end end private def environment_list environments = [] unless @name_args.nil? || @name_args.empty? @name_args.each { |name| environments << name } else environments = Chef::Environment.list end end def constraint_list(environments) constraints = {} environments.each do |env,url| # Because you cannot modify the default environment I filter it out here. unless env == "_default" envdata = Chef::Environment.load(env) ver = envdata.cookbook_versions constraints[env] = ver end end constraints end def cookbook_list(constraints) result = {} constraints.each { |env, cb| result.merge!(cb) } result end def matrix_output(cookbooks, constraints) rows = [ '' ] environments = [] constraints.each { |e,v| environments << e.to_s } columns = environments.count + 1 environments.each { |env| rows << ui.color(env, :bold) } cookbooks.each do |c,v| total = [] environments.each { |n| total << constraints[n][c]} if total.uniq.count == 1 next if config[:mismatch] color = :white else color = :yellow end rows << ui.color(c, :bold) environments.each do |e| tag = constraints[e][c] || "latest" rows << ui.color(tag, color) end end ui.list(rows, :uneven_columns_across, columns) end end end end chef-12.3.0/lib/chef/knife/help.rb0000644000004100000410000000635212520074675016574 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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. # class Chef class Knife class Help < Chef::Knife banner "knife help [list|TOPIC]" def run if name_args.empty? ui.info "Usage: knife SUBCOMMAND (options)" ui.msg "" # This command is atypical, the user is likely not interested in usage of # this command, but knife in general. So hack the banner. opt_parser.banner = "General Knife Options:" ui.msg opt_parser.to_s ui.msg "" ui.info "For further help:" ui.info(<<-MOAR_HELP) knife help list list help topics knife help knife show general knife help knife help TOPIC display the manual for TOPIC knife SUBCOMMAND --help show the options for a command MOAR_HELP exit 1 else @query = name_args.join('-') end case @query when 'topics', 'list' print_help_topics exit 1 when 'intro', 'knife' @topic = 'knife' else @topic = find_manpages_for_query(@query) end manpage_path = find_manpage_path(@topic) exec "man #{manpage_path}" end def help_topics # The list of help topics is generated by a rake task from the available man pages # This constant is provided in help_topics.rb which is automatically required/loaded by the knife subcommand loader. HELP_TOPICS end def print_help_topics ui.info "Available help topics are: " help_topics.collect {|t| t.gsub(/knife-/, '') }.sort.each do |topic| ui.msg " #{topic}" end end def find_manpages_for_query(query) possibilities = help_topics.select do |manpage| ::File.fnmatch("knife-#{query}*", manpage) || ::File.fnmatch("#{query}*", manpage) end if possibilities.empty? ui.error "No help found for '#{query}'" ui.msg "" print_help_topics exit 1 elsif possibilities.size == 1 possibilities.first else ui.info "Multiple help topics match your query. Pick one:" ui.highline.choose(*possibilities) end end def find_manpage_path(topic) if ::File.exists?(::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT)) # If we've provided the man page in the gem, give that return ::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT) else # Otherwise, we'll just be using MANPATH topic end end end end end chef-12.3.0/lib/chef/knife/client_list.rb0000644000004100000410000000215212520074675020147 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientList < Knife deps do require 'chef/api_client' require 'chef/json_compat' end banner "knife client list (options)" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run output(format_list_for_display(Chef::ApiClient.list)) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_search.rb0000644000004100000410000000276012520074675022022 0ustar www-datawww-data# Author:: Adam Jacob () # 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 CookbookSiteSearch < Knife banner "knife cookbook site search QUERY (options)" category "cookbook site" def run output(search_cookbook(name_args[0])) end def search_cookbook(query, items=10, start=0, cookbook_collection={}) cookbooks_url = "https://supermarket.chef.io/api/v1/search?q=#{query}&items=#{items}&start=#{start}" cr = noauth_rest.get_rest(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook end new_start = start + cr["items"].length if new_start < cr["total"] search_cookbook(query, items, new_start, cookbook_collection) else cookbook_collection end end end end end chef-12.3.0/lib/chef/knife/edit.rb0000644000004100000410000000421112520074675016561 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Edit < Chef::ChefFS::Knife banner "knife edit [PATTERN1 ... PATTERNn]" category "path-based" deps do require 'chef/chef_fs/file_system' require 'chef/chef_fs/file_system/not_found_error' end option :local, :long => '--local', :boolean => true, :description => "Show local files instead of remote" def run # Get the matches (recursively) error = false pattern_args.each do |pattern| Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result| if result.dir? ui.error "#{format_path(result)}: is a directory" if pattern.exact_path error = true else begin new_value = edit_text(result.read, File.extname(result.name)) if new_value result.write(new_value) output "Updated #{format_path(result)}" else output "#{format_path(result)} unchanged!" end rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e ui.error "#{format_path(e.entry)}: #{e.reason}." error = true rescue Chef::ChefFS::FileSystem::NotFoundError => e ui.error "#{format_path(e.entry)}: No such file or directory" error = true end end end end if error exit 1 end end def edit_text(text, extension) if (!config[:disable_editing]) Tempfile.open([ 'knife-edit-', extension ]) do |file| # Write the text to a temporary file file.write(text) file.close # Let the user edit the temporary file if !system("#{config[:editor]} #{file.path}") raise "Please set EDITOR environment variable" end result_text = IO.read(file.path) return result_text if result_text != text end end end end end end chef-12.3.0/lib/chef/knife/configure_client.rb0000644000004100000410000000322712520074675021161 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ConfigureClient < Knife banner "knife configure client DIRECTORY" def run unless @config_dir = @name_args[0] ui.fatal "You must provide the directory to put the files in" show_usage exit(1) end ui.info("Creating client configuration") FileUtils.mkdir_p(@config_dir) ui.info("Writing client.rb") File.open(File.join(@config_dir, "client.rb"), "w") do |file| file.puts('log_level :info') file.puts('log_location STDOUT') file.puts("chef_server_url '#{Chef::Config[:chef_server_url]}'") file.puts("validation_client_name '#{Chef::Config[:validation_client_name]}'") end ui.info("Writing validation.pem") File.open(File.join(@config_dir, 'validation.pem'), "w") do |validation| validation.puts(IO.read(Chef::Config[:validation_key])) end end end end end chef-12.3.0/lib/chef/knife/client_create.rb0000644000004100000410000000452212520074675020442 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientCreate < Knife deps do require 'chef/api_client' require 'chef/json_compat' end option :file, :short => "-f FILE", :long => "--file FILE", :description => "Write the key to a file" option :admin, :short => "-a", :long => "--admin", :description => "Create the client as an admin", :boolean => true option :validator, :long => "--validator", :description => "Create the client as a validator", :boolean => true banner "knife client create CLIENT (options)" def run @client_name = @name_args[0] if @client_name.nil? show_usage ui.fatal("You must specify a client name") exit 1 end client_hash = { "name" => @client_name, "admin" => !!config[:admin], "validator" => !!config[:validator] } output = Chef::ApiClient.from_hash(edit_hash(client_hash)) # Chef::ApiClient.save will try to create a client and if it # exists will update it instead silently. client = output.save # We only get a private_key on client creation, not on client update. if client['private_key'] ui.info("Created #{output}") if config[:file] File.open(config[:file], "w") do |f| f.print(client['private_key']) end else puts client['private_key'] end else ui.error "Client '#{client['name']}' already exists" exit 1 end end end end end chef-12.3.0/lib/chef/knife/cookbook_download.rb0000644000004100000410000001071512520074675021337 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2009, 2010 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 CookbookDownload < Knife attr_reader :version attr_accessor :cookbook_name deps do require 'chef/cookbook_version' end banner "knife cookbook download COOKBOOK [VERSION] (options)" option :latest, :short => "-N", :long => "--latest", :description => "The version of the cookbook to download", :boolean => true option :download_directory, :short => "-d DOWNLOAD_DIRECTORY", :long => "--dir DOWNLOAD_DIRECTORY", :description => "The directory to download the cookbook into", :default => Dir.pwd option :force, :short => "-f", :long => "--force", :description => "Force download over the download directory if it exists" # TODO: tim/cw: 5-23-2010: need to implement knife-side # specificity for downloads - need to implement --platform and # --fqdn here def run @cookbook_name, @version = @name_args if @cookbook_name.nil? show_usage ui.fatal("You must specify a cookbook name") exit 1 elsif @version.nil? @version = determine_version if @version.nil? ui.fatal("No such cookbook found") exit 1 end end ui.info("Downloading #{@cookbook_name} cookbook version #{@version}") cookbook = rest.get_rest("cookbooks/#{@cookbook_name}/#{@version}") manifest = cookbook.manifest basedir = File.join(config[:download_directory], "#{@cookbook_name}-#{cookbook.version}") if File.exists?(basedir) if config[:force] Chef::Log.debug("Deleting #{basedir}") FileUtils.rm_rf(basedir) else ui.fatal("Directory #{basedir} exists, use --force to overwrite") exit end end Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment| next unless manifest.has_key?(segment) ui.info("Downloading #{segment}") manifest[segment].each do |segment_file| dest = File.join(basedir, segment_file['path'].gsub('/', File::SEPARATOR)) Chef::Log.debug("Downloading #{segment_file['path']} to #{dest}") FileUtils.mkdir_p(File.dirname(dest)) rest.sign_on_redirect = false tempfile = rest.get_rest(segment_file['url'], true) FileUtils.mv(tempfile.path, dest) end end ui.info("Cookbook downloaded to #{basedir}") end def determine_version if available_versions.nil? nil elsif available_versions.size == 1 @version = available_versions.first elsif config[:latest] @version = available_versions.last else ask_which_version end end def available_versions @available_versions ||= begin versions = Chef::CookbookVersion.available_versions(@cookbook_name) unless versions.nil? versions.map! { |version| Chef::Version.new(version) } versions.sort! end versions end @available_versions end def ask_which_version question = "Which version do you want to download?\n" valid_responses = {} available_versions.each_with_index do |version, index| valid_responses[(index + 1).to_s] = version question << "#{index + 1}. #{@cookbook_name} #{version}\n" end question += "\n" response = ask_question(question).strip unless @version = valid_responses[response] ui.error("'#{response}' is not a valid value.") exit(1) end @version end end end end chef-12.3.0/lib/chef/knife/environment_delete.rb0000644000004100000410000000225012520074675021523 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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 EnvironmentDelete < Knife deps do require 'chef/environment' require 'chef/json_compat' end banner "knife environment delete ENVIRONMENT (options)" def run env_name = @name_args[0] if env_name.nil? show_usage ui.fatal("You must specify an environment name") exit 1 end delete_object(Chef::Environment, env_name) end end end end chef-12.3.0/lib/chef/knife/data_bag_create.rb0000644000004100000410000000452412520074675020710 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2009-2010 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' require 'chef/knife/data_bag_secret_options' class Chef class Knife class DataBagCreate < Knife include DataBagSecretOptions deps do require 'chef/data_bag' require 'chef/encrypted_data_bag_item' end banner "knife data bag create BAG [ITEM] (options)" category "data bag" def run @data_bag_name, @data_bag_item_name = @name_args if @data_bag_name.nil? show_usage ui.fatal("You must specify a data bag name") exit 1 end begin Chef::DataBag.validate_name!(@data_bag_name) rescue Chef::Exceptions::InvalidDataBagName => e ui.fatal(e.message) exit(1) end # create the data bag begin rest.post_rest("data", { "name" => @data_bag_name }) ui.info("Created data_bag[#{@data_bag_name}]") rescue Net::HTTPServerException => e raise unless e.to_s =~ /^409/ ui.info("Data bag #{@data_bag_name} already exists") end # if an item is specified, create it, as well if @data_bag_item_name create_object({ "id" => @data_bag_item_name }, "data_bag_item[#{@data_bag_item_name}]") do |output| item = Chef::DataBagItem.from_hash( if encryption_secret_provided? Chef::EncryptedDataBagItem.encrypt_data_bag_item(output, read_secret) else output end) item.data_bag(@data_bag_name) rest.post_rest("data/#{@data_bag_name}", item) end end end end end end chef-12.3.0/lib/chef/knife/user_list.rb0000644000004100000410000000213612520074675017651 0ustar www-datawww-data# # 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 '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 chef-12.3.0/lib/chef/knife/list.rb0000644000004100000410000001157712520074675016624 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class List < Chef::ChefFS::Knife banner "knife list [-dfR1p] [PATTERN1 ... PATTERNn]" category "path-based" deps do require 'chef/chef_fs/file_system' require 'highline' end option :recursive, :short => '-R', :boolean => true, :description => "List directories recursively" option :bare_directories, :short => '-d', :boolean => true, :description => "When directories match the pattern, do not show the directories' children" option :local, :long => '--local', :boolean => true, :description => "List local directory instead of remote" option :flat, :short => '-f', :long => '--flat', :boolean => true, :description => "Show a list of filenames rather than the prettified ls-like output normally produced" option :one_column, :short => '-1', :boolean => true, :description => "Show only one column of results" option :trailing_slashes, :short => '-p', :boolean => true, :description => "Show trailing slashes after directories" attr_accessor :exit_code def run patterns = name_args.length == 0 ? [""] : name_args # Get the top-level matches args = pattern_args_from(patterns) all_results = parallelize(pattern_args_from(patterns)) do |pattern| pattern_results = Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).to_a if pattern_results.first && !pattern_results.first.exists? && pattern.exact_path ui.error "#{format_path(pattern_results.first)}: No such file or directory" self.exit_code = 1 end pattern_results end.flatten(1).to_a # Process directories if !config[:bare_directories] dir_results = parallelize(all_results.select { |result| result.dir? }) do |result| add_dir_result(result) end.flatten(1) else dir_results = [] end # Process all other results results = all_results.select { |result| result.exists? && (!result.dir? || config[:bare_directories]) }.to_a # Flatten out directory results if necessary if config[:flat] dir_results.each do |result, children| results += children end dir_results = [] end # Sort by path for happy output results = results.sort_by { |result| result.path } dir_results = dir_results.sort_by { |result| result[0].path } # Print! if results.length == 0 && dir_results.length == 1 results = dir_results[0][1] dir_results = [] end print_result_paths results printed_something = results.length > 0 dir_results.each do |result, children| if printed_something output "" else printed_something = true end output "#{format_path(result)}:" print_results(children.map { |result| maybe_add_slash(result.name, result.dir?) }.sort, "") end exit self.exit_code if self.exit_code end def add_dir_result(result) begin children = result.children.sort_by { |child| child.name } rescue Chef::ChefFS::FileSystem::NotFoundError => e ui.error "#{format_path(e.entry)}: No such file or directory" return [] end result = [ [ result, children ] ] if config[:recursive] child_dirs = children.select { |child| child.dir? } result += parallelize(child_dirs) { |child| add_dir_result(child) }.flatten(1).to_a end result end def print_result_paths(results, indent = "") print_results(results.map { |result| maybe_add_slash(format_path(result), result.dir?) }, indent) end def print_results(results, indent) return if results.length == 0 print_space = results.map { |result| result.length }.max + 2 if config[:one_column] || !stdout.isatty columns = 0 else columns = HighLine::SystemExtensions.terminal_size[0] end current_line = '' results.each do |result| if current_line.length > 0 && current_line.length + print_space > columns output current_line.rstrip current_line = '' end if current_line.length == 0 current_line << indent end current_line << result current_line << (' ' * (print_space - result.length)) end output current_line.rstrip if current_line.length > 0 end def maybe_add_slash(path, is_dir) if config[:trailing_slashes] && is_dir "#{path}/" else path end end end end end chef-12.3.0/lib/chef/knife/raw.rb0000644000004100000410000000606012520074675016431 0ustar www-datawww-datarequire 'chef/knife' class Chef class Knife class Raw < Chef::Knife banner "knife raw REQUEST_PATH" deps do require 'chef/json_compat' require 'chef/config' require 'chef/http' require 'chef/http/authenticator' require 'chef/http/cookie_manager' require 'chef/http/decompressor' require 'chef/http/json_output' end option :method, :long => '--method METHOD', :short => '-m METHOD', :default => "GET", :description => "Request method (GET, POST, PUT or DELETE). Default: GET" option :pretty, :long => '--[no-]pretty', :boolean => true, :default => true, :description => "Pretty-print JSON output. Default: true" option :input, :long => '--input FILE', :short => '-i FILE', :description => "Name of file to use for PUT or POST" option :proxy_auth, :long => '--proxy-auth', :boolean => true, :default => false, :description => "Use webui proxy authentication. Client key must be the webui key." class RawInputServerAPI < Chef::HTTP def initialize(options = {}) options[:client_name] ||= Chef::Config[:node_name] options[:signing_key_filename] ||= Chef::Config[:client_key] super(Chef::Config[:chef_server_url], options) end use Chef::HTTP::JSONOutput use Chef::HTTP::CookieManager use Chef::HTTP::Decompressor use Chef::HTTP::Authenticator use Chef::HTTP::RemoteRequestID end def run if name_args.length == 0 show_usage ui.fatal("You must provide the path you want to hit on the server") exit(1) elsif name_args.length > 1 show_usage ui.fatal("Only one path accepted for knife raw") exit(1) end path = name_args[0] data = false if config[:input] data = IO.read(config[:input]) end begin method = config[:method].to_sym headers = {'Content-Type' => 'application/json'} if config[:proxy_auth] headers['x-ops-request-source'] = 'web' end if config[:pretty] chef_rest = RawInputServerAPI.new result = chef_rest.request(method, name_args[0], headers, data) unless result.is_a?(String) result = Chef::JSONCompat.to_json_pretty(result) end else chef_rest = RawInputServerAPI.new(:raw_output => true) result = chef_rest.request(method, name_args[0], headers, data) end output result rescue Timeout::Error => e ui.error "Server timeout" exit 1 rescue Net::HTTPServerException => e ui.error "Server responded with error #{e.response.code} \"#{e.response.message}\"" ui.error "Error Body: #{e.response.body}" if e.response.body && e.response.body != '' exit 1 end end end # class Raw end end chef-12.3.0/lib/chef/knife/serve.rb0000644000004100000410000000322112520074675016760 0ustar www-datawww-datarequire 'chef/knife' require 'chef/local_mode' class Chef class Knife class Serve < Knife banner 'knife serve (options)' option :repo_mode, :long => '--repo-mode MODE', :description => "Specifies the local repository layout. Values: static (only environments/roles/data_bags/cookbooks), everything (includes nodes/clients/users), hosted_everything (includes acls/groups/etc. for Enterprise/Hosted Chef). Default: everything/hosted_everything" option :chef_repo_path, :long => '--chef-repo-path PATH', :description => 'Overrides the location of chef repo. Default is specified by chef_repo_path in the config' option :chef_zero_host, :long => '--chef-zero-host IP', :description => 'Overrides the host upon which chef-zero listens. Default is 127.0.0.1.' def configure_chef super Chef::Config.local_mode = true Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode] # --chef-repo-path forcibly overrides all other paths if config[:chef_repo_path] Chef::Config.chef_repo_path = config[:chef_repo_path] %w(acl client cookbook container data_bag environment group node role user).each do |variable_name| Chef::Config.delete("#{variable_name}_path".to_sym) end end end def run server = Chef::LocalMode.chef_zero_server begin output "Serving files from:\n#{Chef::LocalMode.chef_fs.fs_description}" server.stop server.start(stdout) # to print header ensure server.stop end end end end end chef-12.3.0/lib/chef/knife/node_delete.rb0000644000004100000410000000217612520074675020113 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeDelete < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node delete NODE (options)" def run @node_name = @name_args[0] if @node_name.nil? show_usage ui.fatal("You must specify a node name") exit 1 end delete_object(Chef::Node, @node_name) end end end end chef-12.3.0/lib/chef/knife/environment_show.rb0000644000004100000410000000237712520074675021253 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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 EnvironmentShow < Knife include Knife::Core::MultiAttributeReturnOption deps do require 'chef/environment' require 'chef/json_compat' end banner "knife environment show ENVIRONMENT (options)" def run env_name = @name_args[0] if env_name.nil? show_usage ui.fatal("You must specify an environment name") exit 1 end env = Chef::Environment.load(env_name) output(format_for_display(env)) end end end end chef-12.3.0/lib/chef/knife/role_list.rb0000644000004100000410000000213412520074675017632 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleList < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife role list (options)" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run output(format_list_for_display(Chef::Role.list)) end end end end chef-12.3.0/lib/chef/knife/client_reregister.rb0000644000004100000410000000304712520074675021353 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientReregister < Knife deps do require 'chef/api_client' require 'chef/json_compat' end banner "knife client reregister CLIENT (options)" option :file, :short => "-f FILE", :long => "--file FILE", :description => "Write the key to a file" def run @client_name = @name_args[0] if @client_name.nil? show_usage ui.fatal("You must specify a client name") exit 1 end client = Chef::ApiClient.reregister(@client_name) Chef::Log.debug("Updated client data: #{client.inspect}") key = client.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 chef-12.3.0/lib/chef/knife/download.rb0000644000004100000410000000400612520074675017445 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Download < Chef::ChefFS::Knife banner "knife download PATTERNS" category "path-based" deps do require 'chef/chef_fs/command_line' end option :recurse, :long => '--[no-]recurse', :boolean => true, :default => true, :description => "List directories recursively." option :purge, :long => '--[no-]purge', :boolean => true, :default => false, :description => "Delete matching local files and directories that do not exist remotely." option :force, :long => '--[no-]force', :boolean => true, :default => false, :description => "Force upload of files even if they match (quicker and harmless, but doesn't print out what it changed)" option :dry_run, :long => '--dry-run', :short => '-n', :boolean => true, :default => false, :description => "Don't take action, only print what would happen" option :diff, :long => '--[no-]diff', :boolean => true, :default => true, :description => 'Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff' option :cookbook_version, :long => '--cookbook-version VERSION', :description => 'Version of cookbook to download (if there are multiple versions and cookbook_versions is false)' def run if name_args.length == 0 show_usage ui.fatal("Must specify at least one argument. If you want to download everything in this directory, type \"knife download .\"") exit 1 end error = false pattern_args.each do |pattern| if Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, config, ui, proc { |entry| format_path(entry) }) error = true end end if error exit 1 end end end end end chef-12.3.0/lib/chef/knife/delete.rb0000644000004100000410000000627112520074675017106 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Delete < Chef::ChefFS::Knife banner "knife delete [PATTERN1 ... PATTERNn]" category "path-based" deps do require 'chef/chef_fs/file_system' end option :recurse, :short => '-r', :long => '--[no-]recurse', :boolean => true, :default => false, :description => "Delete directories recursively." option :both, :long => '--both', :boolean => true, :default => false, :description => "Delete both the local and remote copies." option :local, :long => '--local', :boolean => true, :default => false, :description => "Delete the local copy (leave the remote copy)." def run if name_args.length == 0 show_usage ui.fatal("Must specify at least one argument. If you want to delete everything in this directory, type \"knife delete --recurse .\"") exit 1 end # Get the matches (recursively) error = false if config[:local] pattern_args.each do |pattern| Chef::ChefFS::FileSystem.list(local_fs, pattern).each do |result| if delete_result(result) error = true end end end elsif config[:both] pattern_args.each do |pattern| Chef::ChefFS::FileSystem.list_pairs(pattern, chef_fs, local_fs).each do |chef_result, local_result| if delete_result(chef_result, local_result) error = true end end end else # Remote only pattern_args.each do |pattern| Chef::ChefFS::FileSystem.list(chef_fs, pattern).each do |result| if delete_result(result) error = true end end end end if error exit 1 end end def format_path_with_root(entry) root = entry.root == chef_fs ? " (remote)" : " (local)" "#{format_path(entry)}#{root}" end def delete_result(*results) deleted_any = false found_any = false error = false results.each do |result| begin result.delete(config[:recurse]) deleted_any = true found_any = true rescue Chef::ChefFS::FileSystem::NotFoundError # This is not an error unless *all* of them were not found rescue Chef::ChefFS::FileSystem::MustDeleteRecursivelyError => e ui.error "#{format_path_with_root(e.entry)} must be deleted recursively! Pass -r to knife delete." found_any = true error = true rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e ui.error "#{format_path_with_root(e.entry)} #{e.reason}." found_any = true error = true end end if deleted_any output("Deleted #{format_path(results[0])}") elsif !found_any ui.error "#{format_path(results[0])}: No such file or directory" error = true end error end end end end chef-12.3.0/lib/chef/knife/user_edit.rb0000644000004100000410000000262412520074675017625 0ustar www-datawww-data# # 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 '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 chef-12.3.0/lib/chef/knife/role_run_list_replace.rb0000644000004100000410000000326412520074675022216 0ustar www-datawww-data# Author:: Adam Jacob () # Author:: William Albenzi () # 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 RoleRunListReplace < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role run_list replace [ROLE] [OLD_ENTRY] [NEW_ENTRY] " def replace_in_env_run_list(role, environment, old_entry, new_entry) nlist = [] role.run_list_for(environment).each do |entry| if entry == old_entry nlist << new_entry else nlist << entry end end role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = "_default" old_entry = @name_args[1] new_entry = @name_args[2] replace_in_env_run_list(role, environment, old_entry, new_entry) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_list.rb0000644000004100000410000000347612520074675021535 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 CookbookSiteList < Knife banner "knife cookbook site list (options)" category "cookbook site" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" def run if config[:with_uri] cookbooks = Hash.new get_cookbook_list.each{ |k,v| cookbooks[k] = v['cookbook'] } ui.output(format_for_display(cookbooks)) else ui.msg(ui.list(get_cookbook_list.keys.sort, :columns_down)) end end def get_cookbook_list(items=10, start=0, cookbook_collection={}) cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}" cr = noauth_rest.get_rest(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook end new_start = start + cr["items"].length if new_start < cr["total"] get_cookbook_list(items, new_start, cookbook_collection) else cookbook_collection end end end end end chef-12.3.0/lib/chef/knife/role_env_run_list_set.rb0000644000004100000410000000424112520074675022242 0ustar www-datawww-data# # Author:: Mike Fiedler () # Author:: William Albenzi () # Copyright:: Copyright (c) 2013 Mike Fiedler # 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 RoleEnvRunListSet < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role env_run_list set [ROLE] [ENVIRONMENT] [ENTRIES]" # Clears out any existing env_run_list_items and sets them to the # specified entries def set_env_run_list(role, environment, entries) nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end entries.each { |e| nlist << e } role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = @name_args[1] if @name_args.size < 2 ui.fatal "You must supply both a role name and an environment run list." show_usage exit 1 elsif @name_args.size > 2 # Check for nested lists and create a single plain one entries = @name_args[2..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[2].split(',').map { |e| e.strip } end set_env_run_list(role, environment, entries ) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/role_edit.rb0000644000004100000410000000217412520074675017610 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleEdit < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role edit ROLE (options)" def run @role_name = @name_args[0] if @role_name.nil? show_usage ui.fatal("You must specify a role name") exit 1 end ui.edit_object(Chef::Role, @role_name) end end end end chef-12.3.0/lib/chef/knife/cookbook_list.rb0000644000004100000410000000277012520074675020505 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # Copyright:: Copyright (c) 2009, 2010, 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 'chef/knife' class Chef class Knife class CookbookList < Knife banner "knife cookbook list (options)" option :with_uri, :short => "-w", :long => "--with-uri", :description => "Show corresponding URIs" option :all_versions, :short => "-a", :long => "--all", :description => "Show all available versions." def run env = config[:environment] num_versions = config[:all_versions] ? "num_versions=all" : "num_versions=1" api_endpoint = env ? "/environments/#{env}/cookbooks?#{num_versions}" : "/cookbooks?#{num_versions}" cookbook_versions = rest.get_rest(api_endpoint) ui.output(format_cookbook_list_for_display(cookbook_versions)) end end end end chef-12.3.0/lib/chef/knife/ssl_check.rb0000644000004100000410000002017112520074675017575 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/knife' require 'chef/config' class Chef class Knife class SslCheck < Chef::Knife deps do require 'pp' require 'socket' require 'uri' require 'chef/http/ssl_policies' require 'openssl' end banner "knife ssl check [URL] (options)" def initialize(*args) @host = nil @verify_peer_socket = nil @ssl_policy = HTTP::DefaultSSLPolicy super end def uri @uri ||= begin Chef::Log.debug("Checking SSL cert on #{given_uri}") URI.parse(given_uri) end end def given_uri (name_args[0] or Chef::Config.chef_server_url) end def host uri.host end def port uri.port end def validate_uri unless host && port invalid_uri! end rescue URI::Error invalid_uri! end def invalid_uri! ui.error("Given URI: `#{given_uri}' is invalid") show_usage exit 1 end def verify_peer_socket @verify_peer_socket ||= begin tcp_connection = TCPSocket.new(host, port) OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context) end end def verify_peer_ssl_context @verify_peer_ssl_context ||= begin verify_peer_context = OpenSSL::SSL::SSLContext.new @ssl_policy.apply_to(verify_peer_context) verify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_PEER verify_peer_context end end def noverify_socket @noverify_socket ||= begin tcp_connection = TCPSocket.new(host, port) OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context) end end def noverify_peer_ssl_context @noverify_peer_ssl_context ||= begin noverify_peer_context = OpenSSL::SSL::SSLContext.new @ssl_policy.apply_to(noverify_peer_context) noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE noverify_peer_context end end def verify_X509 cert_debug_msg = "" trusted_certificates.each do |cert_name| message = check_X509_certificate(cert_name) unless message.nil? cert_debug_msg << File.expand_path(cert_name) + ": " + message + "\n" end end unless cert_debug_msg.empty? debug_invalid_X509(cert_debug_msg) end true # Maybe the bad certs won't hurt... end def verify_cert ui.msg("Connecting to host #{host}:#{port}") verify_peer_socket.connect true rescue OpenSSL::SSL::SSLError => e ui.error "The SSL certificate of #{host} could not be verified" Chef::Log.debug e.message debug_invalid_cert false end def verify_cert_host verify_peer_socket.post_connection_check(host) true rescue OpenSSL::SSL::SSLError => e ui.error "The SSL cert is signed by a trusted authority but is not valid for the given hostname" Chef::Log.debug(e) debug_invalid_host false end def debug_invalid_X509(cert_debug_msg) ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n") debug_ssl_settings debug_chef_ssl_config ui.warn(<<-BAD_CERTS) There are invalid certificates in your trusted_certs_dir. OpenSSL will not use the following certificates when verifying SSL connections: #{cert_debug_msg} #{ui.color("TO FIX THESE WARNINGS:", :bold)} We are working on documentation for resolving common issues uncovered here. * If the certificate is generated by the server, you may try redownloading the server's certificate. By default, the certificate is stored in the following location on the host where your chef-server runs: /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir}) using SSH/SCP or some other secure method, then re-run this command to confirm that the server's certificate is now trusted. BAD_CERTS # @TODO: ^ needs URL once documentation is posted. end def debug_invalid_cert noverify_socket.connect issuer_info = noverify_socket.peer_cert.issuer ui.msg("Certificate issuer data: #{issuer_info}") ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n") debug_ssl_settings debug_chef_ssl_config ui.err(<<-ADVICE) #{ui.color("TO FIX THIS ERROR:", :bold)} If the server you are connecting to uses a self-signed certificate, you must configure chef to trust that server's certificate. By default, the certificate is stored in the following location on the host where your chef-server runs: /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir}) using SSH/SCP or some other secure method, then re-run this command to confirm that the server's certificate is now trusted. ADVICE end def debug_invalid_host noverify_socket.connect subject = noverify_socket.peer_cert.subject cn_field_tuple = subject.to_a.find {|field| field[0] == "CN" } cn = cn_field_tuple[1] ui.error("You are attempting to connect to: '#{host}'") ui.error("The server's certificate belongs to '#{cn}'") ui.err(<<-ADVICE) #{ui.color("TO FIX THIS ERROR:", :bold)} The solution for this issue depends on your networking configuration. If you are able to connect to this server using the hostname #{cn} instead of #{host}, then you can resolve this issue by updating chef_server_url in your configuration file. If you are not able to connect to the server using the hostname #{cn} you will have to update the certificate on the server to use the correct hostname. ADVICE end def debug_ssl_settings ui.err "OpenSSL Configuration:" ui.err "* Version: #{OpenSSL::OPENSSL_VERSION}" ui.err "* Certificate file: #{OpenSSL::X509::DEFAULT_CERT_FILE}" ui.err "* Certificate directory: #{OpenSSL::X509::DEFAULT_CERT_DIR}" end def debug_chef_ssl_config ui.err "Chef SSL Configuration:" ui.err "* ssl_ca_path: #{configuration.ssl_ca_path.inspect}" ui.err "* ssl_ca_file: #{configuration.ssl_ca_file.inspect}" ui.err "* trusted_certs_dir: #{configuration.trusted_certs_dir.inspect}" end def configuration Chef::Config end def run validate_uri if verify_X509 && verify_cert && verify_cert_host ui.msg "Successfully verified certificates from `#{host}'" else exit 1 end end private def trusted_certificates if configuration.trusted_certs_dir && Dir.exist?(configuration.trusted_certs_dir) Dir.glob(File.join(configuration.trusted_certs_dir, "*.{crt,pem}")) else [] end end def check_X509_certificate(cert_file) store = OpenSSL::X509::Store.new cert = OpenSSL::X509::Certificate.new(IO.read(File.expand_path(cert_file))) begin store.add_cert(cert) # test if the store can verify the cert we just added unless store.verify(cert) # true if verified, false if not return store.error_string end rescue OpenSSL::X509::StoreError => e return e.message end return nil end end end end chef-12.3.0/lib/chef/knife/xargs.rb0000644000004100000410000002121412520074675016762 0ustar www-datawww-datarequire 'chef/chef_fs/knife' class Chef class Knife class Xargs < Chef::ChefFS::Knife banner "knife xargs [COMMAND]" category "path-based" deps do require 'chef/chef_fs/file_system' require 'chef/chef_fs/file_system/not_found_error' end # TODO modify to remote-only / local-only pattern (more like delete) option :local, :long => '--local', :boolean => true, :description => "Xargs local files instead of remote" option :patterns, :long => '--pattern [PATTERN]', :short => '-p [PATTERN]', :description => "Pattern on command line (if these are not specified, a list of patterns is expected on standard input). Multiple patterns may be passed in this way.", :arg_arity => [1,-1] option :diff, :long => '--[no-]diff', :default => true, :boolean => true, :description => "Whether to show a diff when files change (default: true)" option :dry_run, :long => '--dry-run', :boolean => true, :description => "Prevents changes from actually being uploaded to the server." option :force, :long => '--[no-]force', :boolean => true, :default => false, :description => "Force upload of files even if they are not changed (quicker and harmless, but doesn't print out what it changed)" option :replace_first, :long => '--replace-first REPLACESTR', :short => '-J REPLACESTR', :description => "String to replace with filenames. -J will only replace the FIRST occurrence of the replacement string." option :replace_all, :long => '--replace REPLACESTR', :short => '-I REPLACESTR', :description => "String to replace with filenames. -I will replace ALL occurrence of the replacement string." option :max_arguments_per_command, :long => '--max-args MAXARGS', :short => '-n MAXARGS', :description => "Maximum number of arguments per command line." option :max_command_line, :long => '--max-chars LENGTH', :short => '-s LENGTH', :description => "Maximum size of command line, in characters" option :verbose_commands, :short => '-t', :description => "Print command to be run on the command line" option :null_separator, :short => '-0', :boolean => true, :description => "Use the NULL character (\0) as a separator, instead of whitespace" def run error = false # Get the matches (recursively) files = [] pattern_args_from(get_patterns).each do |pattern| Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result| if result.dir? # TODO option to include directories ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it." else files << result ran = false # If the command would be bigger than max command line, back it off a bit # and run a slightly smaller command (with one less arg) if config[:max_command_line] command, tempfiles = create_command(files) begin if command.length > config[:max_command_line].to_i if files.length > 1 command, tempfiles_minus_one = create_command(files[0..-2]) begin error = true if xargs_files(command, tempfiles_minus_one) files = [ files[-1] ] ran = true ensure destroy_tempfiles(tempfiles) end else error = true if xargs_files(command, tempfiles) files = [ ] ran = true end end ensure destroy_tempfiles(tempfiles) end end # If the command has hit the limit for the # of arguments, run it if !ran && config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i command, tempfiles = create_command(files) begin error = true if xargs_files(command, tempfiles) files = [] ran = true ensure destroy_tempfiles(tempfiles) end end end end end # Any leftovers commands shall be run if files.size > 0 command, tempfiles = create_command(files) begin error = true if xargs_files(command, tempfiles) ensure destroy_tempfiles(tempfiles) end end if error exit 1 end end def get_patterns if config[:patterns] [ config[:patterns] ].flatten elsif config[:null_separator] stdin.binmode stdin.read.split("\000") else stdin.read.split(/\s+/) end end def create_command(files) command = name_args.join(' ') # Create the (empty) tempfiles tempfiles = {} begin # Create the temporary files files.each do |file| tempfile = Tempfile.new(file.name) tempfiles[tempfile] = { :file => file } end rescue destroy_tempfiles(files) raise end # Create the command paths = tempfiles.keys.map { |tempfile| tempfile.path }.join(' ') if config[:replace_all] final_command = command.gsub(config[:replace_all], paths) elsif config[:replace_first] final_command = command.sub(config[:replace_first], paths) else final_command = "#{command} #{paths}" end [final_command, tempfiles] end def destroy_tempfiles(tempfiles) # Unlink the files now that we're done with them tempfiles.keys.each { |tempfile| tempfile.close! } end def xargs_files(command, tempfiles) error = false # Create the temporary files tempfiles.each_pair do |tempfile, file| begin value = file[:file].read file[:value] = value tempfile.open tempfile.write(value) tempfile.close rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e ui.error "#{format_path(e.entry)}: #{e.reason}." error = true tempfile.close! tempfiles.delete(tempfile) next rescue Chef::ChefFS::FileSystem::NotFoundError => e ui.error "#{format_path(e.entry)}: No such file or directory" error = true tempfile.close! tempfiles.delete(tempfile) next end end return error if error && tempfiles.size == 0 # Run the command if config[:verbose_commands] || Chef::Config[:verbosity] && Chef::Config[:verbosity] >= 1 output sub_filenames(command, tempfiles) end command_output = `#{command}` command_output = sub_filenames(command_output, tempfiles) stdout.write command_output # Check if the output is different tempfiles.each_pair do |tempfile, file| # Read the new output new_value = IO.binread(tempfile.path) # Upload the output if different if config[:force] || new_value != file[:value] if config[:dry_run] output "Would update #{format_path(file[:file])}" else file[:file].write(new_value) output "Updated #{format_path(file[:file])}" end end # Print a diff of what was uploaded if config[:diff] && new_value != file[:value] old_file = Tempfile.open(file[:file].name) begin old_file.write(file[:value]) old_file.close diff = `diff -u #{old_file.path} #{tempfile.path}` diff.gsub!(old_file.path, "#{format_path(file[:file])} (old)") diff.gsub!(tempfile.path, "#{format_path(file[:file])} (new)") stdout.write diff ensure old_file.close! end end end error end def sub_filenames(str, tempfiles) tempfiles.each_pair do |tempfile, file| str = str.gsub(tempfile.path, format_path(file[:file])) end str end end end end chef-12.3.0/lib/chef/knife/cookbook_delete.rb0000644000004100000410000001212112520074675020763 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 CookbookDelete < Knife attr_accessor :cookbook_name, :version deps do require 'chef/cookbook_version' end option :all, :short => '-a', :long => '--all', :boolean => true, :description => 'delete all versions' option :purge, :short => '-p', :long => '--purge', :boolean => true, :description => 'Permanently remove files from backing data store' banner "knife cookbook delete COOKBOOK VERSION (options)" def run confirm("Files that are common to multiple cookbooks are shared, so purging the files may disable other cookbooks. Are you sure you want to purge files instead of just deleting the cookbook") if config[:purge] @cookbook_name, @version = name_args if @cookbook_name && @version delete_explicit_version elsif @cookbook_name && config[:all] delete_all_versions elsif @cookbook_name && @version.nil? delete_without_explicit_version elsif @cookbook_name.nil? show_usage ui.fatal("You must provide the name of the cookbook to delete") exit(1) end end def delete_explicit_version delete_object(Chef::CookbookVersion, "#{@cookbook_name} version #{@version}", "cookbook") do delete_request("cookbooks/#{@cookbook_name}/#{@version}") end end def delete_all_versions confirm("Do you really want to delete all versions of #{@cookbook_name}") delete_all_without_confirmation end def delete_all_without_confirmation # look up the available versions again just in case the user # got to the list of versions to delete and selected 'all' # and also a specific version @available_versions = nil Array(available_versions).each do |version| delete_version_without_confirmation(version) end end def delete_without_explicit_version if available_versions.nil? # we already logged an error or 2 about it, so just bail exit(1) elsif available_versions.size == 1 @version = available_versions.first delete_explicit_version else versions_to_delete = ask_which_versions_to_delete delete_versions_without_confirmation(versions_to_delete) end end def available_versions @available_versions ||= rest.get_rest("cookbooks/#{@cookbook_name}").map do |name, url_and_version| url_and_version["versions"].map {|url_by_version| url_by_version["version"]} end.flatten rescue Net::HTTPServerException => e if e.to_s =~ /^404/ ui.error("Cannot find a cookbook named #{@cookbook_name} to delete") nil else raise end end def ask_which_versions_to_delete question = "Which version(s) do you want to delete?\n" valid_responses = {} available_versions.each_with_index do |version, index| valid_responses[(index + 1).to_s] = version question << "#{index + 1}. #{@cookbook_name} #{version}\n" end valid_responses[(available_versions.size + 1).to_s] = :all question << "#{available_versions.size + 1}. All versions\n\n" responses = ask_question(question).split(',').map { |response| response.strip } if responses.empty? ui.error("No versions specified, exiting") exit(1) end versions = responses.map do |response| if version = valid_responses[response] version else ui.error("#{response} is not a valid choice, skipping it") end end versions.compact end def delete_version_without_confirmation(version) object = delete_request("cookbooks/#{@cookbook_name}/#{version}") output(format_for_display(object)) if config[:print_after] ui.info("Deleted cookbook[#{@cookbook_name}][#{version}]") end def delete_versions_without_confirmation(versions) versions.each do |version| if version == :all delete_all_without_confirmation break else delete_version_without_confirmation(version) end end end private def delete_request(path) path += "?purge=true" if config[:purge] rest.delete_rest(path) end end end end chef-12.3.0/lib/chef/knife/cookbook_bulk_delete.rb0000644000004100000410000000505612520074675022011 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010 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 CookbookBulkDelete < Knife deps do require 'chef/knife/cookbook_delete' require 'chef/cookbook_version' end option :purge, :short => '-p', :long => '--purge', :boolean => true, :description => 'Permanently remove files from backing data store' banner "knife cookbook bulk delete REGEX (options)" def run unless regex_str = @name_args.first ui.fatal("You must supply a regular expression to match the results against") exit 42 end regex = Regexp.new(regex_str) all_cookbooks = Chef::CookbookVersion.list cookbooks_names = all_cookbooks.keys.grep(regex) cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash } ui.msg "All versions of the following cookbooks will be deleted:" ui.msg "" ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down) ui.msg "" unless config[:yes] ui.confirm("Do you really want to delete these cookbooks") if config[:purge] ui.msg("Files that are common to multiple cookbooks are shared, so purging the files may break other cookbooks.") ui.confirm("Are you sure you want to purge files instead of just deleting the cookbooks") end ui.msg "" end cookbooks_names.each do |cookbook_name| versions = rest.get_rest("cookbooks/#{cookbook_name}")[cookbook_name]["versions"].map {|v| v["version"]}.flatten versions.each do |version| object = rest.delete_rest("cookbooks/#{cookbook_name}/#{version}#{config[:purge] ? "?purge=true" : ""}") ui.info("Deleted cookbook #{cookbook_name.ljust(25)} [#{version}]") end end end end end end chef-12.3.0/lib/chef/knife/search.rb0000644000004100000410000001447512520074675017116 0ustar www-datawww-data# # Author:: Adam Jacob () # 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' require 'chef/knife/core/node_presenter' class Chef class Knife class Search < Knife include Knife::Core::MultiAttributeReturnOption deps do require 'chef/node' require 'chef/environment' require 'chef/api_client' require 'chef/search/query' end include Knife::Core::NodeFormattingOptions banner "knife search INDEX QUERY (options)" option :sort, :short => "-o SORT", :long => "--sort SORT", :description => "The order to sort the results in", :default => nil option :start, :short => "-b ROW", :long => "--start ROW", :description => "The row to start returning results at", :default => 0, :proc => lambda { |i| i.to_i } option :rows, :short => "-R INT", :long => "--rows INT", :description => "The number of rows to return", :default => nil, :proc => lambda { |i| i.to_i } option :run_list, :short => "-r", :long => "--run-list", :description => "Show only the run list" option :id_only, :short => "-i", :long => "--id-only", :description => "Show only the ID of matching objects" option :query, :short => "-q QUERY", :long => "--query QUERY", :description => "The search query; useful to protect queries starting with -" option :filter_result, :short => "-f FILTER", :long => "--filter-result FILTER", :description => "Only bring back specific attributes of the matching objects; for example: \"ServerName=name, Kernel=kernel.version\"" def run read_cli_args fuzzify_query if @type == 'node' ui.use_presenter Knife::Core::NodePresenter end q = Chef::Search::Query.new escaped_query = URI.escape(@query, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) result_items = [] result_count = 0 search_args = Hash.new search_args[:sort] = config[:sort] if config[:sort] search_args[:start] = config[:start] if config[:start] search_args[:rows] = config[:rows] if config[:rows] if config[:filter_result] search_args[:filter_result] = create_result_filter(config[:filter_result]) elsif (not ui.config[:attribute].nil?) && (not ui.config[:attribute].empty?) search_args[:filter_result] = create_result_filter_from_attributes(ui.config[:attribute]) end begin q.search(@type, escaped_query, search_args) do |item| formatted_item = Hash.new if item.is_a?(Hash) # doing a little magic here to set the correct name formatted_item[item["__display_name"]] = item.reject{|k| k == "__display_name"} else formatted_item = format_for_display(item) end result_items << formatted_item result_count += 1 end rescue Net::HTTPServerException => e msg = Chef::JSONCompat.from_json(e.response.body)["error"].first ui.error("knife search failed: #{msg}") exit 1 end if ui.interchange? output({:results => result_count, :rows => result_items}) else ui.log "#{result_count} items found" ui.log("\n") result_items.each do |item| output(item) unless config[:id_only] ui.msg("\n") end end end end def read_cli_args if config[:query] if @name_args[1] ui.error "please specify query as an argument or an option via -q, not both" ui.msg opt_parser exit 1 end @type = name_args[0] @query = config[:query] else case name_args.size when 0 ui.error "no query specified" ui.msg opt_parser exit 1 when 1 @type = "node" @query = name_args[0] when 2 @type = name_args[0] @query = name_args[1] end end end def fuzzify_query if @query !~ /:/ @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*" end end # This method turns a set of key value pairs in a string into the appropriate data structure that the # chef-server search api is expecting. # expected input is in the form of: # -f "return_var1=path.to.attribute, return_var2=shorter.path" # # a more concrete example might be: # -f "env=chef_environment, ruby_platform=languages.ruby.platform" # # The end result is a hash where the key is a symbol in the hash (the return variable) # and the path is an array with the path elements as strings (in order) # See lib/chef/search/query.rb for more examples of this. def create_result_filter(filter_string) final_filter = Hash.new filter_string.gsub!(" ", "") filters = filter_string.split(",") filters.each do |f| return_id, attr_path = f.split("=") final_filter[return_id.to_sym] = attr_path.split(".") end return final_filter end def create_result_filter_from_attributes(filter_array) final_filter = Hash.new filter_array.each do |f| final_filter[f] = f.split(".") end # adding magic filter so we can actually pull the name as before final_filter["__display_name"] = [ "name" ] return final_filter end end end end chef-12.3.0/lib/chef/knife/environment_from_file.rb0000644000004100000410000000430712520074675022230 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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. # class Chef class Knife class EnvironmentFromFile < Knife deps do require 'chef/environment' require 'chef/knife/core/object_loader' end banner "knife environment from file FILE [FILE..] (options)" option :all, :short => "-a", :long => "--all", :description => "Upload all environments" def loader @loader ||= Knife::Core::ObjectLoader.new(Chef::Environment, ui) end def environments_path @environments_path ||= "environments" end def find_all_environments loader.find_all_objects("./#{environments_path}/") end def load_all_environments environments = find_all_environments if environments.empty? ui.fatal("Unable to find any environment files in '#{environments_path}'") exit(1) end environments.each do |env| load_environment(env) end end def load_environment(env) updated = loader.load_from("environments", env) updated.save output(format_for_display(updated)) if config[:print_after] ui.info("Updated Environment #{updated.name}") end def run if config[:all] == true load_all_environments else if @name_args[0].nil? show_usage ui.fatal("You must specify a file to load") exit 1 end @name_args.each do |arg| load_environment(arg) end end end end end end chef-12.3.0/lib/chef/knife/recipe_list.rb0000644000004100000410000000171312520074675020142 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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::Knife::RecipeList < Chef::Knife banner "knife recipe list [PATTERN]" def run recipes = rest.get_rest('cookbooks/_recipes') if pattern = @name_args.first recipes = recipes.grep(Regexp.new(pattern)) end output(recipes) end end chef-12.3.0/lib/chef/knife/client_delete.rb0000644000004100000410000000321612520074675020440 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 ClientDelete < Knife deps do require 'chef/api_client' require 'chef/json_compat' end option :delete_validators, :short => "-D", :long => "--delete-validators", :description => "Force deletion of client if it's a validator" banner "knife client delete CLIENT (options)" def run @client_name = @name_args[0] if @client_name.nil? show_usage ui.fatal("You must specify a client name") exit 1 end delete_object(Chef::ApiClient, @client_name, 'client') { object = Chef::ApiClient.load(@client_name) if object.validator unless config[:delete_validators] ui.fatal("You must specify --delete-validators to delete the validator client #{@client_name}") exit 2 end end object.destroy } end end end end chef-12.3.0/lib/chef/knife/user_delete.rb0000644000004100000410000000220012520074675020130 0ustar www-datawww-data# # 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 '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 chef-12.3.0/lib/chef/knife/index_rebuild.rb0000644000004100000410000001035112520074675020453 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 IndexRebuild < Knife banner "knife index rebuild (options)" option :yes, :short => "-y", :long => "--yes", :boolean => true, :description => "don't bother to ask if I'm sure" def run api_info = grab_api_info if unsupported_version?(api_info) unsupported_server_message(api_info) exit 1 else deprecated_server_message nag output rest.post_rest("/search/reindex", {}) end end def grab_api_info # Since we don't yet have any endpoints that implement an # OPTIONS handler, we need to get our version header # information in a more roundabout way. We'll try to query # for a node we know won't exist; the 404 response that comes # back will give us what we want dummy_node = "knife_index_rebuild_test_#{rand(1000000)}" rest.get_rest("/nodes/#{dummy_node}") rescue Net::HTTPServerException => exception r = exception.response parse_api_info(r) end # Only Chef 11+ servers will have version information in their # headers, and only those servers will lack an API endpoint for # index rebuilding. def unsupported_version?(api_info) !!api_info["version"] end def unsupported_server_message(api_info) ui.error("Rebuilding the index is not available via knife for #{server_type(api_info)}s version 11.0.0 and above.") ui.info("Instead, run the '#{ctl_command(api_info)} reindex' command on the server itself.") end def deprecated_server_message ui.warn("'knife index rebuild' has been removed for Chef 11+ servers. It will continue to work for prior versions, however.") end def nag ui.info("This operation is destructive. Rebuilding the index may take some time.") ui.confirm("Continue") end # Chef 11 (and above) servers return various pieces of # information about the server in an +x-ops-api-info+ header. # This is a +;+ delimited string of key / value pairs, separated # by +=+. # # Given a Net::HTTPResponse object, this method extracts this # information (if present), and returns it as a hash. If no # such header is found, an empty hash is returned. def parse_api_info(response) value = response["x-ops-api-info"] if value kv = value.split(";") kv.inject({}) do |acc, pair| k, v = pair.split("=") acc[k] = v acc end else {} end end # Given an API info hash (see +#parse_api_info(response)+), # return a string describing the kind of server we're # interacting with (based on the +flavor+ field) def server_type(api_info) case api_info["flavor"] when "osc" "Open Source Chef Server" when "opc" "Private Chef Server" else # Generic fallback "Chef Server" end end # Given an API info hash (see +#parse_api_info(response)+), # return the name of the "server-ctl" command for the kind of # server we're interacting with (based on the +flavor+ field) def ctl_command(api_info) case api_info["flavor"] when "osc" "chef-server-ctl" when "opc" "private-chef-ctl" else # Generic fallback "chef-server-ctl" end end end end end chef-12.3.0/lib/chef/knife/data_bag_secret_options.rb0000644000004100000410000001211012520074675022473 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 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 'mixlib/cli' require 'chef/config' require 'chef/encrypted_data_bag_item/check_encrypted' class Chef class Knife module DataBagSecretOptions include Mixlib::CLI include Chef::EncryptedDataBagItem::CheckEncrypted # The config object is populated by knife#merge_configs with knife.rb `knife[:*]` config values, but they do # not overwrite the command line properties. It does mean, however, that `knife[:secret]` and `--secret-file` # passed at the same time populate both `config[:secret]` and `config[:secret_file]`. We cannot differentiate # the valid case (`knife[:secret]` in config file and `--secret-file` on CL) and the invalid case (`--secret` # and `--secret-file` on the CL) - thats why I'm storing the CL options in a different config key if they # are provided. def self.included(base) base.option :secret, :short => "-s SECRET", :long => "--secret ", :description => "The secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret'", # Need to store value from command line in separate variable - knife#merge_configs populates same keys # on config object from :proc => Proc.new { |s| set_cl_secret(s) } base.option :secret_file, :long => "--secret-file SECRET_FILE", :description => "A file containing the secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret_file'", :proc => Proc.new { |sf| set_cl_secret_file(sf) } base.option :encrypt, :long => "--encrypt", :description => "If 'secret' or 'secret_file' is present in your config, then encrypt data bags using it", :boolean => true, :default => false end def encryption_secret_provided? base_encryption_secret_provided? end def encryption_secret_provided_ignore_encrypt_flag? base_encryption_secret_provided?(false) end def read_secret # Moving the non 'compile-time' requires into here to speed up knife command loading # IE, if we are not running 'knife data bag *' we don't need to load 'chef/encrypted_data_bag_item' require 'chef/encrypted_data_bag_item' if has_cl_secret? config[:secret] elsif has_cl_secret_file? Chef::EncryptedDataBagItem.load_secret(config[:secret_file]) elsif secret = knife_config[:secret] secret else secret_file = knife_config[:secret_file] Chef::EncryptedDataBagItem.load_secret(secret_file) end end def validate_secrets if has_cl_secret? && has_cl_secret_file? ui.fatal("Please specify only one of --secret, --secret-file") exit(1) end if knife_config[:secret] && knife_config[:secret_file] ui.fatal("Please specify only one of 'secret' or 'secret_file' in your config file") exit(1) end end private ## # Determine if the user has specified an appropriate secret for encrypting data bag items. # @returns boolean def base_encryption_secret_provided?(need_encrypt_flag = true) validate_secrets return true if has_cl_secret? || has_cl_secret_file? if need_encrypt_flag if config[:encrypt] unless knife_config[:secret] || knife_config[:secret_file] ui.fatal("No secret or secret_file specified in config, unable to encrypt item.") exit(1) end return true end return false elsif knife_config[:secret] || knife_config[:secret_file] # Certain situations (show and bootstrap) don't need a --encrypt flag to use the config file secret return true end return false end def has_cl_secret? Chef::Config[:knife].has_key?(:cl_secret) end def self.set_cl_secret(s) Chef::Config[:knife][:cl_secret] = s end def has_cl_secret_file? Chef::Config[:knife].has_key?(:cl_secret_file) end def self.set_cl_secret_file(sf) Chef::Config[:knife][:cl_secret_file] = sf end def knife_config Chef::Config.key?(:knife) ? Chef::Config[:knife] : {} end end end end chef-12.3.0/lib/chef/knife/role_env_run_list_remove.rb0000644000004100000410000000321012520074675022737 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleEnvRunListRemove < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role env_run_list remove [ROLE] [ENVIRONMENT] [ENTRIES]" def remove_from_env_run_list(role, environment, item_to_remove) nlist = [] role.run_list_for(environment).each do |entry| nlist << entry unless entry == item_to_remove #unless entry == @name_args[2] # nlist << entry #end end role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = @name_args[1] item_to_remove = @name_args[2] remove_from_env_run_list(role, environment, item_to_remove) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/role_env_run_list_add.rb0000644000004100000410000000521012520074675022174 0ustar www-datawww-data# Author:: Adam Jacob () # Author:: William Albenzi () # 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 RoleEnvRunListAdd < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role env_run_list add [ROLE] [ENVIRONMENT] [ENTRY[,ENTRY]] (options)" option :after, :short => "-a ITEM", :long => "--after ITEM", :description => "Place the ENTRY in the run list after ITEM" def add_to_env_run_list(role, environment, entries, after=nil) if after nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end role.run_list_for(environment).each do |entry| nlist << entry if entry == after entries.each { |e| nlist << e } end end role.env_run_lists_add(environment => nlist) else nlist = [] unless role.env_run_lists.key?(environment) role.env_run_lists_add(environment => nlist) end role.run_list_for(environment).each do |entry| nlist << entry end entries.each { |e| nlist << e } role.env_run_lists_add(environment => nlist) end end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = @name_args[1] if @name_args.size > 2 # Check for nested lists and create a single plain one entries = @name_args[2..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[2].split(',').map { |e| e.strip } end add_to_env_run_list(role, environment, entries, config[:after]) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/role_env_run_list_replace.rb0000644000004100000410000000331412520074675023062 0ustar www-datawww-data# Author:: Adam Jacob () # Author:: William Albenzi () # 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 RoleEnvRunListReplace < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role env_run_list replace [ROLE] [ENVIRONMENT] [OLD_ENTRY] [NEW_ENTRY] " def replace_in_env_run_list(role, environment, old_entry, new_entry) nlist = [] role.run_list_for(environment).each do |entry| if entry == old_entry nlist << new_entry else nlist << entry end end role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = @name_args[1] old_entry = @name_args[2] new_entry = @name_args[3] replace_in_env_run_list(role, environment, old_entry, new_entry) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/environment_create.rb0000644000004100000410000000267112520074675021533 0ustar www-datawww-data# # Author:: Stephen Delano () # Copyright:: Copyright (c) 2010 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 EnvironmentCreate < Knife deps do require 'chef/environment' require 'chef/json_compat' end banner "knife environment create ENVIRONMENT (options)" option :description, :short => "-d DESCRIPTION", :long => "--description DESCRIPTION", :description => "The environment description" def run env_name = @name_args[0] if env_name.nil? show_usage ui.fatal("You must specify an environment name") exit 1 end env = Chef::Environment.new env.name(env_name) env.description(config[:description]) if config[:description] create_object(env) end end end end chef-12.3.0/lib/chef/knife/cookbook_site_show.rb0000644000004100000410000000350712520074675021535 0ustar www-datawww-data# Author:: Adam Jacob () # 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 CookbookSiteShow < Knife banner "knife cookbook site show COOKBOOK [VERSION] (options)" category "cookbook site" def run output(format_for_display(get_cookbook_data)) end def get_cookbook_data case @name_args.length when 1 noauth_rest.get_rest("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}") when 2 noauth_rest.get_rest("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].gsub('.', '_')}") end end def get_cookbook_list(items=10, start=0, cookbook_collection={}) cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}" cr = noauth_rest.get_rest(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook end new_start = start + cr["items"].length if new_start < cr["total"] get_cookbook_list(items, new_start, cookbook_collection) else cookbook_collection end end end end end chef-12.3.0/lib/chef/knife/role_run_list_remove.rb0000644000004100000410000000315612520074675022100 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 RoleRunListRemove < Knife deps do require 'chef/role' require 'chef/json_compat' end banner "knife role run_list remove [ROLE] [ENTRY]" def remove_from_env_run_list(role, environment, item_to_remove) nlist = [] role.run_list_for(environment).each do |entry| nlist << entry unless entry == item_to_remove #unless entry == @name_args[2] # nlist << entry #end end role.env_run_lists_add(environment => nlist) end def run role = Chef::Role.load(@name_args[0]) role.name(@name_args[0]) environment = "_default" item_to_remove = @name_args[1] remove_from_env_run_list(role, environment, item_to_remove) role.save config[:env_run_list] = true output(format_for_display(role)) end end end end chef-12.3.0/lib/chef/knife/cookbook_metadata_from_file.rb0000644000004100000410000000232312520074675023326 0ustar www-datawww-data# # # Author:: Adam Jacob () # Author:: Matthew Kent () # Copyright:: Copyright (c) 2009 Opscode, Inc. # Copyright:: Copyright (c) 2010 Matthew Kent # 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 CookbookMetadataFromFile < Knife deps do require 'chef/cookbook/metadata' end banner "knife cookbook metadata from FILE (options)" def run file = @name_args[0] cookbook = File.basename(File.dirname(file)) @metadata = Chef::Knife::CookbookMetadata.new @metadata.generate_metadata_from_file(cookbook, file) end end end end chef-12.3.0/lib/chef/knife/cookbook_metadata.rb0000644000004100000410000000707712520074675021317 0ustar www-datawww-data# # # Author:: Adam Jacob () # 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 CookbookMetadata < Knife deps do require 'chef/cookbook_loader' require 'chef/cookbook/metadata' end banner "knife cookbook metadata COOKBOOK (options)" option :cookbook_path, :short => "-o PATH:PATH", :long => "--cookbook-path PATH:PATH", :description => "A colon-separated path to look for cookbooks in", :proc => lambda { |o| o.split(":") } option :all, :short => "-a", :long => "--all", :description => "Generate metadata for all cookbooks, rather than just a single cookbook" def run config[:cookbook_path] ||= Chef::Config[:cookbook_path] if config[:all] cl = Chef::CookbookLoader.new(config[:cookbook_path]) cl.load_cookbooks cl.each do |cname, cookbook| generate_metadata(cname.to_s) end else cookbook_name = @name_args[0] if cookbook_name.nil? || cookbook_name.empty? ui.error "You must specify the cookbook to generate metadata for, or use the --all option." exit 1 end generate_metadata(cookbook_name) end end def generate_metadata(cookbook) Array(config[:cookbook_path]).reverse.each do |path| file = File.expand_path(File.join(path, cookbook, 'metadata.rb')) if File.exists?(file) generate_metadata_from_file(cookbook, file) else validate_metadata_json(path, cookbook) end end end def generate_metadata_from_file(cookbook, file) ui.info("Generating metadata for #{cookbook} from #{file}") md = Chef::Cookbook::Metadata.new md.name(cookbook) md.from_file(file) json_file = File.join(File.dirname(file), 'metadata.json') File.open(json_file, "w") do |f| f.write(Chef::JSONCompat.to_json_pretty(md)) end generated = true Chef::Log.debug("Generated #{json_file}") rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax." ui.stderr.puts "in #{file}:" ui.stderr.puts ui.stderr.puts e.message exit 1 end def validate_metadata_json(path, cookbook) json_file = File.join(path, cookbook, 'metadata.json') if File.exist?(json_file) Chef::Cookbook::Metadata.validate_json(IO.read(json_file)) end rescue Exceptions::ObsoleteDependencySyntax, Exceptions::InvalidVersionConstraint => e ui.stderr.puts "ERROR: The cookbook '#{cookbook}' contains invalid or obsolete metadata syntax." ui.stderr.puts "in #{json_file}:" ui.stderr.puts ui.stderr.puts e.message exit 1 end end end end chef-12.3.0/lib/chef/knife/node_run_list_remove.rb0000644000004100000410000000277712520074675022074 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 NodeRunListRemove < Knife deps do require 'chef/node' require 'chef/json_compat' end banner "knife node run_list remove [NODE] [ENTRY[,ENTRY]] (options)" def run node = Chef::Node.load(@name_args[0]) if @name_args.size > 2 # Check for nested lists and create a single plain one entries = @name_args[1..-1].map do |entry| entry.split(',').map { |e| e.strip } end.flatten else # Convert to array and remove the extra spaces entries = @name_args[1].split(',').map { |e| e.strip } end entries.each { |e| node.run_list.remove(e) } node.save config[:run_list] = true output(format_for_display(node)) end end end end chef-12.3.0/lib/chef/knife/data_bag_delete.rb0000644000004100000410000000264512520074675020711 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 DataBagDelete < Knife deps do require 'chef/data_bag' end banner "knife data bag delete BAG [ITEM] (options)" category "data bag" def run if @name_args.length == 2 delete_object(Chef::DataBagItem, @name_args[1], "data_bag_item") do rest.delete_rest("data/#{@name_args[0]}/#{@name_args[1]}") end elsif @name_args.length == 1 delete_object(Chef::DataBag, @name_args[0], "data_bag") do rest.delete_rest("data/#{@name_args[0]}") end else show_usage ui.fatal("You must specify at least a data bag name") exit 1 end end end end end chef-12.3.0/lib/chef/knife/tag_list.rb0000644000004100000410000000227112520074675017446 0ustar www-datawww-data# # Author:: Ryan Davis () # Author:: Daniel DeLeo () # Author:: Nuo Yan () # Copyright:: Copyright (c) 2011 Ryan Davis and 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 TagList < Knife deps do require 'chef/node' end banner "knife tag list NODE" def run name = @name_args[0] if name.nil? show_usage ui.fatal("You must specify a node name.") exit 1 end node = Chef::Node.load(name) output(node.tags) end end end end chef-12.3.0/lib/chef/rest.rb0000644000004100000410000001472612520074675015531 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2009, 2010 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 'tempfile' require 'chef/http' class Chef class HTTP; end class REST < HTTP; end end require 'chef/http/authenticator' require 'chef/http/decompressor' require 'chef/http/json_input' require 'chef/http/json_to_model_output' require 'chef/http/cookie_manager' require 'chef/http/validate_content_length' require 'chef/config' require 'chef/exceptions' require 'chef/platform/query_helpers' require 'chef/http/remote_request_id' class Chef # == Chef::REST # Chef's custom REST client with built-in JSON support and RSA signed header # authentication. class REST < HTTP # Backwards compatibility for things that use # Chef::REST::RESTRequest or its constants RESTRequest = HTTP::HTTPRequest attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit attr_reader :authenticator # Create a REST client object. The supplied +url+ is used as the base for # all subsequent requests. For example, when initialized with a base url # http://localhost:4000, a call to +get_rest+ with 'nodes' will make an # HTTP GET request to http://localhost:4000/nodes def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key], options={}) signing_key_filename = nil if chef_zero_uri?(url) options = options.dup options[:client_name] = client_name options[:signing_key_filename] = signing_key_filename super(url, options) @decompressor = Decompressor.new(options) @authenticator = Authenticator.new(options) @request_id = RemoteRequestID.new(options) @middlewares << JSONInput.new(options) @middlewares << JSONToModelOutput.new(options) @middlewares << CookieManager.new(options) @middlewares << @decompressor @middlewares << @authenticator @middlewares << @request_id # ValidateContentLength should come after Decompressor # because the order of middlewares is reversed when handling # responses. @middlewares << ValidateContentLength.new(options) end def signing_key_filename authenticator.signing_key_filename end def auth_credentials authenticator.auth_credentials end def client_name authenticator.client_name end def signing_key authenticator.raw_key end def sign_requests? authenticator.sign_requests? end # Send an HTTP GET request to the path # # Using this method to +fetch+ a file is considered deprecated. # # === Parameters # path:: The path to GET # raw:: Whether you want the raw body returned, or JSON inflated. Defaults # to JSON inflated. def get(path, raw=false, headers={}) if raw streaming_request(path, headers) else request(:GET, path, headers) end end alias :get_rest :get alias :delete_rest :delete alias :post_rest :post alias :put_rest :put # Streams a download to a tempfile, then yields the tempfile to a block. # After the download, the tempfile will be closed and unlinked. # If you rename the tempfile, it will not be deleted. # Beware that if the server streams infinite content, this method will # stream it until you run out of disk space. def fetch(path, headers={}) streaming_request(create_url(path), headers) {|tmp_file| yield tmp_file } end alias :api_request :request # Do a HTTP request where no middleware is loaded (e.g. JSON input/output # conversion) but the standard Chef Authentication headers are added to the # request. def raw_http_request(method, path, headers, data) url = create_url(path) method, url, headers, data = @authenticator.handle_request(method, url, headers, data) method, url, headers, data = @request_id.handle_request(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) response.error! unless success_response?(response) return_value rescue Exception => exception log_failed_request(response, return_value) unless response.nil? if exception.respond_to?(:chef_rest_request=) exception.chef_rest_request = rest_request end raise end # Deprecated: # Responsibilities of this method have been split up. The #http_client is # now responsible for making individual requests, while # #retrying_http_errors handles error/retry logic. def retriable_http_request(method, url, req_body, headers) rest_request = Chef::HTTP::HTTPRequest.new(method, url, req_body, headers) Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}") retrying_http_errors(url) do yield rest_request end end # Customized streaming behavior; sets the accepted content type to "*/*" # if not otherwise specified for compatibility purposes def streaming_request(url, headers, &block) headers["Accept"] ||= "*/*" super end alias :retriable_rest_request :retriable_http_request def follow_redirect unless @sign_on_redirect @authenticator.sign_request = false end super ensure @authenticator.sign_request = true end public :create_url ############################################################################ # DEPRECATED ############################################################################ def decompress_body(body) @decompressor.decompress_body(body) end def authentication_headers(method, url, json_body=nil) authenticator.authentication_headers(method, url, json_body) end end end chef-12.3.0/lib/chef/http.rb0000644000004100000410000003354712520074675015535 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010, 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 'tempfile' require 'net/https' require 'uri' require 'chef/http/basic_client' require 'chef/http/socketless_chef_zero_client' require 'chef/monkey_patches/net_http' require 'chef/config' require 'chef/platform/query_helpers' require 'chef/exceptions' class Chef # == Chef::HTTP # Basic HTTP client, with support for adding features via middleware class HTTP # Class for applying middleware behaviors to streaming # responses. Collects stream handlers (if any) from each # middleware. When #handle_chunk is called, the chunk gets # passed to all handlers in turn for processing. class StreamHandler def initialize(middlewares, response) middlewares = middlewares.flatten @stream_handlers = [] middlewares.each do |middleware| stream_handler = middleware.stream_response_handler(response) @stream_handlers << stream_handler unless stream_handler.nil? end end def handle_chunk(next_chunk) # stream handlers handle responses so must be applied in reverse order # (same as #apply_stream_complete_middleware or #apply_response_midddleware) @stream_handlers.reverse.inject(next_chunk) do |chunk, handler| Chef::Log.debug("Chef::HTTP::StreamHandler calling #{handler.class}#handle_chunk") handler.handle_chunk(chunk) end end end def self.middlewares @middlewares ||= [] end def self.use(middleware_class) middlewares << middleware_class end attr_reader :url attr_reader :sign_on_redirect attr_reader :redirect_limit attr_reader :middlewares # Create a HTTP client object. The supplied +url+ is used as the base for # all subsequent requests. For example, when initialized with a base url # http://localhost:4000, a call to +get+ with 'nodes' will make an # HTTP GET request to http://localhost:4000/nodes def initialize(url, options={}) @url = url @default_headers = options[:headers] || {} @sign_on_redirect = true @redirects_followed = 0 @redirect_limit = 10 @middlewares = [] self.class.middlewares.each do |middleware_class| @middlewares << middleware_class.new(options) end end # Send an HTTP HEAD request to the path # # === Parameters # path:: path part of the request URL def head(path, headers={}) request(:HEAD, path, headers) end # Send an HTTP GET request to the path # # === Parameters # path:: The path to GET def get(path, headers={}) request(:GET, path, headers) end # Send an HTTP PUT request to the path # # === Parameters # path:: path part of the request URL def put(path, json, headers={}) request(:PUT, path, headers, json) end # Send an HTTP POST request to the path # # === Parameters # path:: path part of the request URL def post(path, json, headers={}) request(:POST, path, headers, json) end # Send an HTTP DELETE request to the path # # === Parameters # path:: path part of the request URL def delete(path, headers={}) request(:DELETE, path, headers) end # Makes an HTTP request to +path+ with the given +method+, +headers+, and # +data+ (if applicable). def request(method, path, headers={}, data=false) url = create_url(path) method, url, headers, data = apply_request_middleware(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) response, rest_request, return_value = apply_response_middleware(response, rest_request, return_value) response.error! unless success_response?(response) return_value rescue Exception => exception log_failed_request(response, return_value) unless response.nil? if exception.respond_to?(:chef_rest_request=) exception.chef_rest_request = rest_request end raise end # Makes a streaming download request, streaming the response body to a # tempfile. If a block is given, the tempfile is passed to the block and # the tempfile will automatically be unlinked after the block is executed. # # If no block is given, the tempfile is returned, which means it's up to # you to unlink the tempfile when you're done with it. def streaming_request(path, headers={}, &block) url = create_url(path) response, rest_request, return_value = nil, nil, nil tempfile = nil method = :GET method, url, headers, data = apply_request_middleware(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) do |http_response| if http_response.kind_of?(Net::HTTPSuccess) tempfile = stream_to_tempfile(url, http_response) end apply_stream_complete_middleware(http_response, rest_request, return_value) end return nil if response.kind_of?(Net::HTTPRedirection) unless response.kind_of?(Net::HTTPSuccess) response.error! end if block_given? begin yield tempfile ensure tempfile && tempfile.close! end end tempfile rescue Exception => e log_failed_request(response, return_value) unless response.nil? if e.respond_to?(:chef_rest_request=) e.chef_rest_request = rest_request end raise end def http_client(base_url=nil) base_url ||= url if chef_zero_uri?(base_url) SocketlessChefZeroClient.new(base_url) else BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy) end end protected def create_url(path) return path if path.is_a?(URI) if path =~ /^(http|https|chefzero):\/\//i URI.parse(path) elsif path.nil? or path.empty? URI.parse(@url) else # The regular expressions used here are to make sure '@url' does not have # any trailing slashes and 'path' does not have any leading slashes. This # way they are always joined correctly using just one slash. URI.parse(@url.gsub(%r{/+$}, '') + '/' + path.gsub(%r{^/+}, '')) end end def apply_request_middleware(method, url, headers, data) middlewares.inject([method, url, headers, data]) do |req_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_request") middleware.handle_request(*req_data) end end def apply_response_middleware(response, rest_request, return_value) middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_response") middleware.handle_response(*res_data) end end def apply_stream_complete_middleware(response, rest_request, return_value) middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_stream_complete") middleware.handle_stream_complete(*res_data) end end def log_failed_request(response, return_value) return_value ||= {} error_message = "HTTP Request Returned #{response.code} #{response.message}: " error_message << (return_value["error"].respond_to?(:join) ? return_value["error"].join(", ") : return_value["error"].to_s) Chef::Log.info(error_message) end def success_response?(response) response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection) end # Runs a synchronous HTTP request, with no middleware applied (use #request # to have the middleware applied). The entire response will be loaded into memory. def send_http_request(method, url, headers, body, &response_handler) headers = build_headers(method, url, headers, body) retrying_http_errors(url) do client = http_client(url) return_value = nil if block_given? request, response = client.request(method, url, body, headers, &response_handler) else request, response = client.request(method, url, body, headers) {|r| r.read_body } return_value = response.read_body end @last_response = response if response.kind_of?(Net::HTTPSuccess) [response, request, return_value] elsif response.kind_of?(Net::HTTPNotModified) # Must be tested before Net::HTTPRedirection because it's subclass. [response, request, false] elsif redirect_location = redirected_to(response) if [:GET, :HEAD].include?(method) follow_redirect do send_http_request(method, url+redirect_location, headers, body, &response_handler) end else raise Exceptions::InvalidRedirect, "#{method} request was redirected from #{url} to #{redirect_location}. Only GET and HEAD support redirects." end else [response, request, nil] end end end # Wraps an HTTP request with retry logic. # === Arguments # url:: URL of the request, used for error messages def retrying_http_errors(url) http_attempts = 0 begin loop do http_attempts += 1 response, request, return_value = yield # handle HTTP 50X Error if response.kind_of?(Net::HTTPServerError) && !Chef::Config.local_mode if http_retry_count - http_attempts + 1 > 0 sleep_time = 1 + (2 ** http_attempts) + rand(2 ** http_attempts) Chef::Log.error("Server returned error #{response.code} for #{url}, retrying #{http_attempts}/#{http_retry_count} in #{sleep_time}s") sleep(sleep_time) redo end end return [response, request, return_value] end rescue SocketError, Errno::ETIMEDOUT => e if http_retry_count - http_attempts + 1 > 0 Chef::Log.error("Error connecting to #{url}, retry #{http_attempts}/#{http_retry_count}") sleep(http_retry_delay) retry end e.message.replace "Error connecting to #{url} - #{e.message}" raise e rescue Errno::ECONNREFUSED if http_retry_count - http_attempts + 1 > 0 Chef::Log.error("Connection refused connecting to #{url}, retry #{http_attempts}/#{http_retry_count}") sleep(http_retry_delay) retry end raise Errno::ECONNREFUSED, "Connection refused connecting to #{url}, giving up" rescue Timeout::Error if http_retry_count - http_attempts + 1 > 0 Chef::Log.error("Timeout connecting to #{url}, retry #{http_attempts}/#{http_retry_count}") sleep(http_retry_delay) retry end raise Timeout::Error, "Timeout connecting to #{url}, giving up" end end def http_retry_delay config[:http_retry_delay] end def http_retry_count config[:http_retry_count] end def config Chef::Config end def follow_redirect raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit @redirects_followed += 1 Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}") yield ensure @redirects_followed = 0 end private def chef_zero_uri?(uri) uri = URI.parse(uri) unless uri.respond_to?(:scheme) uri.scheme == "chefzero" end def redirected_to(response) return nil unless response.kind_of?(Net::HTTPRedirection) # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this return nil if response.kind_of?(Net::HTTPNotModified) response['location'] end def build_headers(method, url, headers={}, json_body=false) headers = @default_headers.merge(headers) headers['Content-Length'] = json_body.bytesize.to_s if json_body headers.merge!(Chef::Config[:custom_http_headers]) if Chef::Config[:custom_http_headers] headers end def stream_to_tempfile(url, response) tf = Tempfile.open("chef-rest") if Chef::Platform.windows? tf.binmode # required for binary files on Windows platforms end Chef::Log.debug("Streaming download from #{url.to_s} to tempfile #{tf.path}") # Stolen from http://www.ruby-forum.com/topic/166423 # Kudos to _why! stream_handler = StreamHandler.new(middlewares, response) response.read_body do |chunk| tf.write(stream_handler.handle_chunk(chunk)) end tf.close tf rescue Exception tf.close! raise end public ############################################################################ # DEPRECATED ############################################################################ # This is only kept around to provide access to cache control data in # lib/chef/provider/remote_file/http.rb # Find a better API. def last_response @last_response end end end chef-12.3.0/lib/chef/config_fetcher.rb0000644000004100000410000000320412520074675017506 0ustar www-datawww-datarequire 'chef/application' require 'chef/chef_fs/path_utils' require 'chef/http/simple' require 'chef/json_compat' class Chef class ConfigFetcher attr_reader :config_location def initialize(config_location) @config_location = config_location end def fetch_json config_data = read_config begin Chef::JSONCompat.from_json(config_data) rescue Chef::Exceptions::JSON::ParseError => error Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message, 2) end end def read_config if remote_config? fetch_remote_config else read_local_config end end def fetch_remote_config http.get("") rescue SocketError, SystemCallError, Net::HTTPServerException => error Chef::Application.fatal!("Cannot fetch config '#{config_location}': '#{error.class}: #{error.message}", 2) end def read_local_config ::File.read(config_location) rescue Errno::ENOENT => error Chef::Application.fatal!("Cannot load configuration from #{config_location}", 2) rescue Errno::EACCES => error Chef::Application.fatal!("Permissions are incorrect on #{config_location}. Please chmod a+r #{config_location}", 2) end def config_missing? return false if remote_config? # Check if the config file exists Pathname.new(config_location).realpath.to_s false rescue Errno::ENOENT return true end def http Chef::HTTP::Simple.new(config_location) end def remote_config? !!(config_location =~ %r{^(http|https)://}) end end end chef-12.3.0/lib/chef/whitelist.rb0000644000004100000410000000457412520074675016570 0ustar www-datawww-data require 'chef/exceptions' class Chef class Whitelist # filter takes two arguments - the data you want to filter, and a whitelisted array # of keys you want included. You can capture a subtree of the data to filter by # providing a "/"-delimited string of keys. If some key includes "/"-characters, # you must provide an array of keys instead. # # Whitelist.filter( # { "filesystem" => { # "/dev/disk" => { # "size" => "10mb" # }, # "map - autohome" => { # "size" => "10mb" # } # }, # "network" => { # "interfaces" => { # "eth0" => {...}, # "eth1" => {...} # } # } # }, # ["network/interfaces/eth0", ["filesystem", "/dev/disk"]]) # will capture the eth0 and /dev/disk subtrees. def self.filter(data, whitelist=nil) return data if whitelist.nil? new_data = {} whitelist.each do |item| add_data(data, new_data, item) end new_data end # Walk the data has according to the keys provided by the whitelisted item # and add the data to the whitelisting result. def self.add_data(data, new_data, item) parts = to_array(item) all_data = data filtered_data = new_data parts[0..-2].each do |part| unless all_data[part] Chef::Log.warn("Could not find whitelist attribute #{item}.") return nil end filtered_data[part] ||= {} filtered_data = filtered_data[part] all_data = all_data[part] end # Note: You can't do all_data[parts[-1]] here because the value # may be false-y unless all_data.key?(parts[-1]) Chef::Log.warn("Could not find whitelist attribute #{item}.") return nil end filtered_data[parts[-1]] = all_data[parts[-1]] new_data end private_class_method :add_data # Accepts a String or an Array, and returns an Array of String keys that # are used to traverse the data hash. Strings are split on "/", Arrays are # assumed to contain exact keys (that is, Array elements will not be split # by "/"). def self.to_array(item) return item if item.kind_of? Array parts = item.split("/") parts.shift if !parts.empty? && parts[0].empty? parts end private_class_method :to_array end end chef-12.3.0/lib/chef/resources.rb0000644000004100000410000000576012520074675016564 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/apt_package' require 'chef/resource/bash' require 'chef/resource/batch' require 'chef/resource/breakpoint' require 'chef/resource/cookbook_file' require 'chef/resource/chef_gem' require 'chef/resource/cron' require 'chef/resource/csh' require 'chef/resource/deploy' require 'chef/resource/deploy_revision' require 'chef/resource/directory' require 'chef/resource/dpkg_package' require 'chef/resource/dsc_script' require 'chef/resource/dsc_resource' require 'chef/resource/easy_install_package' require 'chef/resource/env' require 'chef/resource/erl_call' require 'chef/resource/execute' require 'chef/resource/file' require 'chef/resource/freebsd_package' require 'chef/resource/ips_package' require 'chef/resource/gem_package' require 'chef/resource/git' require 'chef/resource/group' require 'chef/resource/http_request' require 'chef/resource/homebrew_package' require 'chef/resource/ifconfig' require 'chef/resource/link' require 'chef/resource/log' require 'chef/resource/macports_package' require 'chef/resource/mdadm' require 'chef/resource/mount' require 'chef/resource/ohai' require 'chef/resource/openbsd_package' require 'chef/resource/package' require 'chef/resource/pacman_package' require 'chef/resource/paludis_package' require 'chef/resource/perl' require 'chef/resource/portage_package' require 'chef/resource/powershell_script' require 'chef/resource/python' require 'chef/resource/reboot' require 'chef/resource/registry_key' require 'chef/resource/remote_directory' require 'chef/resource/remote_file' require 'chef/resource/rpm_package' require 'chef/resource/solaris_package' require 'chef/resource/route' require 'chef/resource/ruby' require 'chef/resource/ruby_block' require 'chef/resource/scm' require 'chef/resource/script' require 'chef/resource/service' require 'chef/resource/windows_service' require 'chef/resource/subversion' require 'chef/resource/smartos_package' require 'chef/resource/template' require 'chef/resource/timestamped_deploy' require 'chef/resource/user' require 'chef/resource/whyrun_safe_ruby_block' require 'chef/resource/windows_package' require 'chef/resource/yum_package' require 'chef/resource/lwrp_base' require 'chef/resource/bff_package' begin # Optional resources chef_node, chef_client, machine, machine_image, etc. require 'cheffish' require 'chef/provisioning' rescue LoadError end chef-12.3.0/lib/chef/knife.rb0000644000004100000410000004560312520074675015646 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # 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 'forwardable' require 'chef/version' require 'mixlib/cli' require 'chef/workstation_config_loader' require 'chef/mixin/convert_to_class_name' require 'chef/mixin/path_sanity' require 'chef/knife/core/subcommand_loader' require 'chef/knife/core/ui' require 'chef/local_mode' require 'chef/rest' require 'pp' class Chef class Knife Chef::REST::RESTRequest.user_agent = "Chef Knife#{Chef::REST::RESTRequest::UA_COMMON}" include Mixlib::CLI include Chef::Mixin::PathSanity extend Chef::Mixin::ConvertToClassName extend Forwardable # Backwards Compat: # Ideally, we should not vomit all of these methods into this base class; # instead, they should be accessed by hitting the ui object directly. def_delegator :@ui, :stdout def_delegator :@ui, :stderr def_delegator :@ui, :stdin def_delegator :@ui, :msg def_delegator :@ui, :ask_question def_delegator :@ui, :pretty_print def_delegator :@ui, :output def_delegator :@ui, :format_list_for_display def_delegator :@ui, :format_for_display def_delegator :@ui, :format_cookbook_list_for_display def_delegator :@ui, :edit_data def_delegator :@ui, :edit_hash def_delegator :@ui, :edit_object def_delegator :@ui, :confirm attr_accessor :name_args attr_accessor :ui # Configure mixlib-cli to always separate defaults from user-supplied CLI options def self.use_separate_defaults? true end def self.ui @ui ||= Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {}) end def self.msg(msg="") ui.msg(msg) end def self.reset_config_loader! @@chef_config_dir = nil @config_loader = nil end def self.reset_subcommands! @@subcommands = {} @subcommands_by_category = nil end def self.inherited(subclass) unless subclass.unnamed? subcommands[subclass.snake_case_name] = subclass end end # Explicitly set the category for the current command to +new_category+ # The category is normally determined from the first word of the command # name, but some commands make more sense using two or more words # ===Arguments # new_category::: A String to set the category to (see examples) # ===Examples: # Data bag commands would be in the 'data' category by default. To put them # in the 'data bag' category: # category('data bag') def self.category(new_category) @category = new_category end def self.subcommand_category @category || snake_case_name.split('_').first unless unnamed? end def self.snake_case_name convert_to_snake_case(name.split('::').last) unless unnamed? end def self.common_name snake_case_name.split('_').join(' ') end # Does this class have a name? (Classes created via Class.new don't) def self.unnamed? name.nil? || name.empty? end def self.subcommand_loader @subcommand_loader ||= Knife::SubcommandLoader.new(chef_config_dir) end def self.load_commands @commands_loaded ||= subcommand_loader.load_commands end def self.subcommands @@subcommands ||= {} end def self.subcommands_by_category unless @subcommands_by_category @subcommands_by_category = Hash.new { |hash, key| hash[key] = [] } subcommands.each do |snake_cased, klass| @subcommands_by_category[klass.subcommand_category] << snake_cased end end @subcommands_by_category end # Print the list of subcommands knife knows about. If +preferred_category+ # is given, only subcommands in that category are shown def self.list_commands(preferred_category=nil) load_commands category_desc = preferred_category ? preferred_category + " " : '' msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n" if preferred_category && subcommands_by_category.key?(preferred_category) commands_to_show = {preferred_category => subcommands_by_category[preferred_category]} else commands_to_show = subcommands_by_category end commands_to_show.sort.each do |category, commands| next if category =~ /deprecated/i msg "** #{category.upcase} COMMANDS **" commands.sort.each do |command| msg subcommands[command].banner if subcommands[command] end msg end end # Shared with subclasses @@chef_config_dir = nil def self.config_loader @config_loader ||= WorkstationConfigLoader.new(nil, Chef::Log) end def self.load_config(explicit_config_file) config_loader.explicit_config_file = explicit_config_file config_loader.load ui.warn("No knife configuration file found") if config_loader.no_config_found? config_loader rescue Exceptions::ConfigurationError => e ui.error(ui.color("CONFIGURATION ERROR:", :red) + e.message) exit 1 end def self.chef_config_dir @@chef_config_dir ||= config_loader.chef_config_dir end # Run knife for the given +args+ (ARGV), adding +options+ to the list of # CLI options that the subcommand knows how to handle. # ===Arguments # args::: usually ARGV # options::: A Mixlib::CLI option parser hash. These +options+ are how # subcommands know about global knife CLI options def self.run(args, options={}) # Fallback debug logging. Normally the logger isn't configured until we # read the config, but this means any logging that happens before the # config file is read may be lost. If the KNIFE_DEBUG variable is set, we # setup the logger for debug logging to stderr immediately to catch info # from early in the setup process. if ENV['KNIFE_DEBUG'] Chef::Log.init($stderr) Chef::Log.level(:debug) end load_commands subcommand_class = subcommand_class_from(args) subcommand_class.options = options.merge!(subcommand_class.options) subcommand_class.load_deps instance = subcommand_class.new(args) instance.configure_chef instance.run_with_pretty_exceptions end def self.guess_category(args) category_words = args.select {|arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ } category_words.map! {|w| w.split('-')}.flatten! matching_category = nil while (!matching_category) && (!category_words.empty?) candidate_category = category_words.join(' ') matching_category = candidate_category if subcommands_by_category.key?(candidate_category) matching_category || category_words.pop end matching_category end def self.subcommand_class_from(args) command_words = args.select {|arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ } subcommand_class = nil while ( !subcommand_class ) && ( !command_words.empty? ) snake_case_class_name = command_words.join("_") unless subcommand_class = subcommands[snake_case_class_name] command_words.pop end end # see if we got the command as e.g., knife node-list subcommand_class ||= subcommands[args.first.gsub('-', '_')] subcommand_class || subcommand_not_found!(args) end def self.dependency_loaders @dependency_loaders ||= [] end def self.deps(&block) dependency_loaders << block end def self.load_deps dependency_loaders.each do |dep_loader| dep_loader.call end end private OFFICIAL_PLUGINS = %w[ec2 rackspace windows openstack terremark bluebox] # :nodoc: # Error out and print usage. probably because the arguments given by the # user could not be resolved to a subcommand. def self.subcommand_not_found!(args) ui.fatal("Cannot find sub command for: '#{args.join(' ')}'") if category_commands = guess_category(args) list_commands(category_commands) elsif missing_plugin = ( OFFICIAL_PLUGINS.find {|plugin| plugin == args[0]} ) ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10") ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`") ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK") else list_commands end exit 10 end def self.reset_config_path! @@chef_config_dir = nil end reset_config_path! public # Create a new instance of the current class configured for the given # arguments and options def initialize(argv=[]) super() # having to call super in initialize is the most annoying anti-pattern :( @ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, config) command_name_words = self.class.snake_case_name.split('_') # Mixlib::CLI ignores the embedded name_args @name_args = parse_options(argv) @name_args.delete(command_name_words.join('-')) @name_args.reject! { |name_arg| command_name_words.delete(name_arg) } # knife node run_list add requires that we have extra logic to handle # the case that command name words could be joined by an underscore :/ command_name_words = command_name_words.join('_') @name_args.reject! { |name_arg| command_name_words == name_arg } if config[:help] msg opt_parser exit 1 end # copy Mixlib::CLI over so that it can be configured in knife.rb # config file Chef::Config[:verbosity] = config[:verbosity] end def parse_options(args) super rescue OptionParser::InvalidOption => e puts "Error: " + e.to_s show_usage exit(1) end # Returns a subset of the Chef::Config[:knife] Hash that is relevant to the # currently executing knife command. This is used by #configure_chef to # apply settings from knife.rb to the +config+ hash. def config_file_settings config_file_settings = {} self.class.options.keys.each do |key| config_file_settings[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key) end config_file_settings end # Apply Config in this order: # defaults from mixlib-cli # settings from config file, via Chef::Config[:knife] # config from command line def merge_configs # Apply config file settings on top of mixlib-cli defaults combined_config = default_config.merge(config_file_settings) # Apply user-supplied options on top of the above combination combined_config = combined_config.merge(config) # replace the config hash from mixlib-cli with our own. # Need to use the mutate-in-place #replace method instead of assigning to # the instance variable because other code may have a reference to the # original config hash object. config.replace(combined_config) end # Catch-all method that does any massaging needed for various config # components, such as expanding file paths and converting verbosity level # into log level. def apply_computed_config Chef::Config[:color] = config[:color] case Chef::Config[:verbosity] when 0, nil Chef::Config[:log_level] = :error when 1 Chef::Config[:log_level] = :info else Chef::Config[:log_level] = :debug end Chef::Config[:log_level] = :debug if ENV['KNIFE_DEBUG'] Chef::Config[:node_name] = config[:node_name] if config[:node_name] Chef::Config[:client_key] = config[:client_key] if config[:client_key] Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url] Chef::Config[:environment] = config[:environment] if config[:environment] Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode) Chef::Config.listen = config[:listen] if config.has_key?(:listen) if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path) Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd) end Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host] Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port] # Expand a relative path from the config directory. Config from command # line should already be expanded, and absolute paths will be unchanged. if Chef::Config[:client_key] && config[:config_file] Chef::Config[:client_key] = File.expand_path(Chef::Config[:client_key], File.dirname(config[:config_file])) end Mixlib::Log::Formatter.show_time = false Chef::Log.init(Chef::Config[:log_location]) Chef::Log.level(Chef::Config[:log_level] || :error) if Chef::Config[:node_name] && Chef::Config[:node_name].bytesize > 90 # node names > 90 bytes only work with authentication protocol >= 1.1 # see discussion in config.rb. Chef::Config[:authentication_protocol_version] = "1.1" end end def configure_chef config_loader = self.class.load_config(config[:config_file]) config[:config_file] = config_loader.config_location merge_configs apply_computed_config # This has to be after apply_computed_config so that Mixlib::Log is configured Chef::Log.info("Using configuration from #{config[:config_file]}") if config[:config_file] end def show_usage stdout.puts("USAGE: " + self.opt_parser.to_s) end def run_with_pretty_exceptions(raise_exception = false) unless self.respond_to?(:run) ui.error "You need to add a #run method to your knife command before you can use it" end enforce_path_sanity Chef::LocalMode.with_server_connectivity do run end rescue Exception => e raise if raise_exception || Chef::Config[:verbosity] == 2 humanize_exception(e) exit 100 end def humanize_exception(e) case e when SystemExit raise # make sure exit passes through. when Net::HTTPServerException, Net::HTTPFatalError humanize_http_exception(e) when OpenSSL::SSL::SSLError ui.error "Could not establish a secure connection to the server." ui.info "Use `knife ssl check` to troubleshoot your SSL configuration." ui.info "If your Chef Server uses a self-signed certificate, you can use" ui.info "`knife ssl fetch` to make knife trust the server's certificates." ui.info "" ui.info "Original Exception: #{e.class.name}: #{e.message}" when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError ui.error "Network Error: #{e.message}" ui.info "Check your knife configuration and network settings" when NameError, NoMethodError ui.error "knife encountered an unexpected error" ui.info "This may be a bug in the '#{self.class.common_name}' knife command or plugin" ui.info "Please collect the output of this command with the `-VV` option before filing a bug report." ui.info "Exception: #{e.class.name}: #{e.message}" when Chef::Exceptions::PrivateKeyMissing ui.error "Your private key could not be loaded from #{api_key}" ui.info "Check your configuration file and ensure that your private key is readable" when Chef::Exceptions::InvalidRedirect ui.error "Invalid Redirect: #{e.message}" ui.info "Change your server location in knife.rb to the server's FQDN to avoid unwanted redirections." else ui.error "#{e.class.name}: #{e.message}" end end def humanize_http_exception(e) response = e.response case response when Net::HTTPUnauthorized ui.error "Failed to authenticate to #{server_url} as #{username} with key #{api_key}" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPForbidden ui.error "You authenticated successfully to #{server_url} as #{username} but you are not authorized for this action" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPBadRequest ui.error "The data in your request was invalid" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPNotFound ui.error "The object you are looking for could not be found" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPInternalServerError ui.error "internal server error" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPBadGateway ui.error "bad gateway" ui.info "Response: #{format_rest_error(response)}" when Net::HTTPServiceUnavailable ui.error "Service temporarily unavailable" ui.info "Response: #{format_rest_error(response)}" else ui.error response.message ui.info "Response: #{format_rest_error(response)}" end end def username Chef::Config[:node_name] end def api_key Chef::Config[:client_key] end # Parses JSON from the error response sent by Chef Server and returns the # error message #-- # TODO: this code belongs in Chef::REST def format_rest_error(response) Array(Chef::JSONCompat.from_json(response.body)["error"]).join('; ') rescue Exception response.body end def create_object(object, pretty_name=nil, &block) output = edit_data(object) if Kernel.block_given? output = block.call(output) else output.save end pretty_name ||= output self.msg("Created #{pretty_name}") output(output) if config[:print_after] end def delete_object(klass, name, delete_name=nil, &block) confirm("Do you really want to delete #{name}") if Kernel.block_given? object = block.call else object = klass.load(name) object.destroy end output(format_for_display(object)) if config[:print_after] obj_name = delete_name ? "#{delete_name}[#{name}]" : object self.msg("Deleted #{obj_name}") end def rest @rest ||= begin require 'chef/rest' Chef::REST.new(Chef::Config[:chef_server_url]) end end def noauth_rest @rest ||= begin require 'chef/rest' Chef::REST.new(Chef::Config[:chef_server_url], false, false) end end def server_url Chef::Config[:chef_server_url] end end end chef-12.3.0/lib/chef/dsl/0000755000004100000410000000000012520074675014777 5ustar www-datawww-datachef-12.3.0/lib/chef/dsl/data_query.rb0000644000004100000410000000547412520074675017474 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/search/query' require 'chef/data_bag' require 'chef/data_bag_item' require 'chef/encrypted_data_bag_item' require 'chef/encrypted_data_bag_item/check_encrypted' class Chef module DSL # ==Chef::DSL::DataQuery # Provides DSL for querying data from the chef-server via search or data # bag. module DataQuery include Chef::EncryptedDataBagItem::CheckEncrypted def search(*args, &block) # If you pass a block, or have at least the start argument, do raw result parsing # # Otherwise, do the iteration for the end user if Kernel.block_given? || args.length >= 4 Chef::Search::Query.new.search(*args, &block) else results = Array.new Chef::Search::Query.new.search(*args) do |o| results << o end results end end def data_bag(bag) DataBag.validate_name!(bag.to_s) rbag = DataBag.load(bag) rbag.keys rescue Exception Log.error("Failed to list data bag items in data bag: #{bag.inspect}") raise end def data_bag_item(bag, item, secret=nil) DataBag.validate_name!(bag.to_s) DataBagItem.validate_id!(item) item = DataBagItem.load(bag, item) if encrypted?(item.raw_data) Log.debug("Data bag item looks encrypted: #{bag.inspect} #{item.inspect}") # Try to load the data bag item secret, if secret is not provided. # Chef::EncryptedDataBagItem.load_secret may throw a variety of errors. begin secret ||= EncryptedDataBagItem.load_secret item = EncryptedDataBagItem.new(item.raw_data, secret) rescue Exception Log.error("Failed to load secret for encrypted data bag item: #{bag.inspect} #{item.inspect}") raise end end item rescue Exception Log.error("Failed to load data bag item: #{bag.inspect} #{item.inspect}") raise end end end end # **DEPRECATED** # This used to be part of chef/mixin/language. Load the file to activate the deprecation code. require 'chef/mixin/language' chef-12.3.0/lib/chef/dsl/audit.rb0000644000004100000410000000341212520074675016432 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2014 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' class Chef module DSL module Audit # Can encompass tests in a `control` block or `describe` block # Adds the controls group and block (containing controls to execute) to the runner's list of pending examples def control_group(*args, &block) raise Chef::Exceptions::NoAuditsProvided unless block name = args[0] if name.nil? || name.empty? raise Chef::Exceptions::AuditNameMissing elsif run_context.audits.has_key?(name) raise Chef::Exceptions::AuditControlGroupDuplicate.new(name) end # This DSL will only work in the Recipe class because that exposes the cookbook_name cookbook_name = self.cookbook_name metadata = { cookbook_name: cookbook_name, cookbook_version: self.run_context.cookbook_collection[cookbook_name].version, recipe_name: self.recipe_name, line_number: block.source_location[1] } run_context.audits[name] = Struct.new(:args, :block, :metadata).new(args, block, metadata) end end end end chef-12.3.0/lib/chef/dsl/reboot_pending.rb0000644000004100000410000000617512520074675020333 0ustar www-datawww-data# Author:: Bryan McLellan # Author:: Seth Chisamore # Copyright:: Copyright (c) 2011,2014, Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/dsl/platform_introspection' require 'chef/dsl/registry_helper' class Chef module DSL module RebootPending include Chef::DSL::RegistryHelper include Chef::DSL::PlatformIntrospection # Returns true if the system needs a reboot or is expected to reboot # Note that we will silently miss any other platform-specific reboot notices besides Windows+Ubuntu. def reboot_pending? # don't break when used as a mixin in contexts without #node (e.g. specs). if self.respond_to?(:node, true) && node.run_context.reboot_requested? true elsif platform?("windows") # PendingFileRenameOperations contains pairs (REG_MULTI_SZ) of filenames that cannot be updated # due to a file being in use (usually a temporary file and a system file) # \??\c:\temp\test.sys!\??\c:\winnt\system32\test.sys # http://technet.microsoft.com/en-us/library/cc960241.aspx registry_value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }) || # RebootRequired key contains Update IDs with a value of 1 if they require a reboot. # The existence of RebootRequired alone is sufficient on my Windows 8.1 workstation in Windows Update registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') || # Vista + Server 2008 and newer may have reboots pending from CBS registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired') || # The mere existence of the UpdateExeVolatile key should indicate a pending restart for certain updates # http://support.microsoft.com/kb/832475 (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') && !registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0].nil? && [1,2,3].include?(registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0][:data])) elsif platform?("ubuntu") # This should work for Debian as well if update-notifier-common happens to be installed. We need an API for that. File.exists?('/var/run/reboot-required') else false end end end end end chef-12.3.0/lib/chef/dsl/include_attribute.rb0000644000004100000410000000431212520074675021032 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 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/log' class Chef module DSL module IncludeAttribute # Loads the attribute file specified by the short name of the # file, e.g., loads specified cookbook's # "attributes/mailservers.rb" # if passed # "mailservers" def include_attribute(*attr_file_specs) attr_file_specs.flatten.each do |attr_file_spec| cookbook_name, attr_file = parse_attribute_file_spec(attr_file_spec) if run_context.loaded_fully_qualified_attribute?(cookbook_name, attr_file) Chef::Log.debug("I am not loading attribute file #{cookbook_name}::#{attr_file}, because I have already seen it.") else Chef::Log.debug("Loading Attribute #{cookbook_name}::#{attr_file}") run_context.loaded_attribute(cookbook_name, attr_file) attr_file_path = run_context.resolve_attribute(cookbook_name, attr_file) node.from_file(attr_file_path) end end true end # Takes a attribute file specification, like "apache2" or "mysql::server" # and converts it to a 2 element array of [cookbook_name, attribute_file_name] def parse_attribute_file_spec(file_spec) if match = file_spec.match(/(.+?)::(.+)/) [match[1], match[2]] else [file_spec, "default"] end end end end end # **DEPRECATED** # This used to be part of chef/mixin/language_include_attribute. Load the file to activate the deprecation code. require 'chef/mixin/language_include_attribute' chef-12.3.0/lib/chef/dsl/platform_introspection.rb0000644000004100000410000002335112520074675022134 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef module DSL # == Chef::DSL::PlatformIntrospection # Provides the DSL for platform-dependent switch logic, such as # #value_for_platform. module PlatformIntrospection # Implementation class for determining platform dependent values class PlatformDependentValue # Create a platform dependent value object. # === Arguments # platform_hash (Hash) a hash of the same structure as Chef::Platform, # like this: # { # :debian => {:default => 'the value for all debian'} # [:centos, :redhat, :fedora] => {:default => "value for all EL variants"} # :ubuntu => { :default => "default for ubuntu", '10.04' => "value for 10.04 only"}, # :default => "the default when nothing else matches" # } # * platforms can be specified as Symbols or Strings # * multiple platforms can be grouped by using an Array as the key # * values for platforms need to be Hashes of the form: # {platform_version => value_for_that_version} # * the exception to the above is the default value, which is given as # :default => default_value def initialize(platform_hash) @values = {} platform_hash.each { |platforms, value| set(platforms, value)} end def value_for_node(node) platform, version = node[:platform].to_s, node[:platform_version].to_s # Check if we match a version constraint via Chef::VersionConstraint and Chef::Version::Platform matched_value = match_versions(node) if @values.key?(platform) && @values[platform].key?(version) @values[platform][version] elsif matched_value matched_value elsif @values.key?(platform) && @values[platform].key?("default") @values[platform]["default"] elsif @values.key?("default") @values["default"] else nil end end private def match_versions(node) begin platform, version = node[:platform].to_s, node[:platform_version].to_s return nil unless @values.key?(platform) node_version = Chef::Version::Platform.new(version) key_matches = [] keys = @values[platform].keys keys.each do |k| begin if Chef::VersionConstraint.new(k).include?(node_version) key_matches << k end rescue Chef::Exceptions::InvalidVersionConstraint => e Chef::Log.debug "Caught InvalidVersionConstraint. This means that a key in value_for_platform cannot be interpreted as a Chef::VersionConstraint." Chef::Log.debug(e) end end return @values[platform][version] if key_matches.include?(version) case key_matches.length when 0 return nil when 1 return @values[platform][key_matches.first] else raise "Multiple matches detected for #{platform} with values #{@values}. The matches are: #{key_matches}" end rescue Chef::Exceptions::InvalidCookbookVersion => e # Lets not break because someone passes a weird string like 'default' :) Chef::Log.debug(e) Chef::Log.debug "InvalidCookbookVersion exceptions are common and expected here: the generic constraint matcher attempted to match something which is not a constraint. Moving on to next version or constraint" return nil rescue Chef::Exceptions::InvalidPlatformVersion => e Chef::Log.debug "Caught InvalidPlatformVersion, this means that Chef::Version::Platform does not know how to turn #{node_version} into an x.y.z format" Chef::Log.debug(e) return nil end end def set(platforms, value) if platforms.to_s == 'default' @values["default"] = value else assert_valid_platform_values!(platforms, value) Array(platforms).each { |platform| @values[platform.to_s] = normalize_keys(value)} value end end def normalize_keys(hash) hash.inject({}) do |h, key_value| keys, value = *key_value Array(keys).each do |key| h[key.to_s] = value end h end end def assert_valid_platform_values!(platforms, value) unless value.kind_of?(Hash) msg = "platform dependent values must be specified in the format :platform => {:version => value} " msg << "you gave a value #{value.inspect} for platform(s) #{platforms}" raise ArgumentError, msg end end end # Given a hash similar to the one we use for Platforms, select a value from the hash. Supports # per platform defaults, along with a single base default. Arrays may be passed as hash keys and # will be expanded. # # === Parameters # platform_hash:: A platform-style hash. # # === Returns # value:: Whatever the most specific value of the hash is. def value_for_platform(platform_hash) PlatformDependentValue.new(platform_hash).value_for_node(node) end # Given a list of platforms, returns true if the current recipe is being run on a node with # that platform, false otherwise. # # === Parameters # args:: A list of platforms. Each platform can be in string or symbol format. # # === Returns # true:: If the current platform is in the list # false:: If the current platform is not in the list def platform?(*args) has_platform = false args.flatten.each do |platform| has_platform = true if platform.to_s == node[:platform] end has_platform end # Implementation class for determining platform family dependent values class PlatformFamilyDependentValue # Create a platform family dependent value object. # === Arguments # platform_family_hash (Hash) a map of platform families to values. # like this: # { # :rhel => "value for all EL variants" # :fedora => "value for fedora variants fedora and amazon" , # [:fedora, :rhel] => "value for all known redhat variants" # :debian => "value for debian variants including debian, ubuntu, mint" , # :default => "the default when nothing else matches" # } # * platform families can be specified as Symbols or Strings # * multiple platform families can be grouped by using an Array as the key # * values for platform families can be any object, with no restrictions. Some examples: # - [:stop, :start] # - "mysql-devel" # - { :key => "value" } def initialize(platform_family_hash) @values = {} @values["default"] = nil platform_family_hash.each { |platform_families, value| set(platform_families, value)} end def value_for_node(node) if node.key?(:platform_family) platform_family = node[:platform_family].to_s if @values.key?(platform_family) @values[platform_family] else @values["default"] end else @values["default"] end end private def set(platform_family, value) if platform_family.to_s == 'default' @values["default"] = value else Array(platform_family).each { |family| @values[family.to_s] = value } value end end end # Given a hash mapping platform families to values, select a value from the hash. Supports a single # base default if platform family is not in the map. Arrays may be passed as hash keys and will be # expanded # # === Parameters # platform_family_hash:: A hash in the form { platform_family_name => value } # # === Returns # value:: Whatever the most specific value of the hash is. def value_for_platform_family(platform_family_hash) PlatformFamilyDependentValue.new(platform_family_hash).value_for_node(node) end # Given a list of platform families, returns true if the current recipe is being run on a # node within that platform family, false otherwise. # # === Parameters # args:: A list of platform families. Each platform family can be in string or symbol format. # # === Returns # true:: if the current node platform family is in the list. # false:: if the current node platform family is not in the list. def platform_family?(*args) args.flatten.any? do |platform_family| platform_family.to_s == node[:platform_family] end end end end end # **DEPRECATED** # This used to be part of chef/mixin/language. Load the file to activate the deprecation code. require 'chef/mixin/language' chef-12.3.0/lib/chef/dsl/recipe.rb0000644000004100000410000001530412520074675016576 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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/mixin/convert_to_class_name' require 'chef/exceptions' require 'chef/resource_builder' require 'chef/mixin/shell_out' class Chef module DSL # == Chef::DSL::Recipe # Provides the primary recipe DSL functionality for defining Chef resource # objects via method calls. module Recipe include Chef::Mixin::ShellOut include Chef::Mixin::ConvertToClassName def method_missing(method_symbol, *args, &block) # If we have a definition that matches, we want to use that instead. This should # let you do some really crazy over-riding of "native" types, if you really want # to. if has_resource_definition?(method_symbol) evaluate_resource_definition(method_symbol, *args, &block) elsif have_resource_class_for?(method_symbol) # Otherwise, we're rocking the regular resource call route. declare_resource(method_symbol, args[0], caller[0], &block) else begin super rescue NoMethodError raise NoMethodError, "No resource or method named `#{method_symbol}' for #{describe_self_for_error}" rescue NameError raise NameError, "No resource, method, or local variable named `#{method_symbol}' for #{describe_self_for_error}" end end end def has_resource_definition?(name) run_context.definitions.has_key?(name) end # Processes the arguments and block as a resource definition. def evaluate_resource_definition(definition_name, *args, &block) # This dupes the high level object, but we still need to dup the params new_def = run_context.definitions[definition_name].dup new_def.params = new_def.params.dup new_def.node = run_context.node # This sets up the parameter overrides new_def.instance_eval(&block) if block new_recipe = Chef::Recipe.new(cookbook_name, recipe_name, run_context) new_recipe.params = new_def.params new_recipe.params[:name] = args[0] new_recipe.instance_eval(&new_def.recipe) end # # Instantiates a resource (via #build_resource), then adds it to the # resource collection. Note that resource classes are looked up directly, # so this will create the resource you intended even if the method name # corresponding to that resource has been overridden. # # @param type [Symbol] The type of resource (e.g. `:file` or `:package`) # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2') # @param created_at [String] The caller of the resource. Use `caller[0]` # to get the caller of your function. Defaults to the caller of this # function. # @param resource_attrs_block A block that lets you set attributes of the # resource (it is instance_eval'd on the resource instance). # # @return [Chef::Resource] The new resource. # # @example # declare_resource(:file, '/x/y.txy', caller[0]) do # action :delete # end # # Equivalent to # file '/x/y.txt' do # action :delete # end # def declare_resource(type, name, created_at=nil, &resource_attrs_block) created_at ||= caller[0] resource = build_resource(type, name, created_at, &resource_attrs_block) run_context.resource_collection.insert(resource, resource_type: type, instance_name: name) resource end # # Instantiate a resource of the given +type+ with the given +name+ and # attributes as given in the +resource_attrs_block+. # # The resource is NOT added to the resource collection. # # @param type [Symbol] The type of resource (e.g. `:file` or `:package`) # @param name [String] The name of the resource (e.g. '/x/y.txt' or 'apache2') # @param created_at [String] The caller of the resource. Use `caller[0]` # to get the caller of your function. Defaults to the caller of this # function. # @param resource_attrs_block A block that lets you set attributes of the # resource (it is instance_eval'd on the resource instance). # # @return [Chef::Resource] The new resource. # # @example # build_resource(:file, '/x/y.txy', caller[0]) do # action :delete # end # def build_resource(type, name, created_at=nil, &resource_attrs_block) created_at ||= caller[0] Chef::ResourceBuilder.new( type: type, name: name, created_at: created_at, params: @params, run_context: run_context, cookbook_name: cookbook_name, recipe_name: recipe_name, enclosing_provider: self.is_a?(Chef::Provider) ? self : nil ).build(&resource_attrs_block) end def resource_class_for(snake_case_name) Chef::Resource.resource_for_node(snake_case_name, run_context.node) end def have_resource_class_for?(snake_case_name) not resource_class_for(snake_case_name).nil? rescue NameError false end def describe_self_for_error if respond_to?(:name) %Q[`#{self.class.name} "#{name}"'] elsif respond_to?(:recipe_name) %Q[`#{self.class.name} "#{recipe_name}"'] else to_s end end def exec(args) raise Chef::Exceptions::ResourceNotFound, "exec was called, but you probably meant to use an execute resource. If not, please call Kernel#exec explicitly. The exec block called was \"#{args}\"" end end end end # We require this at the BOTTOM of this file to avoid circular requires (it is used # at runtime but not load time) require 'chef/resource' # **DEPRECATED** # This used to be part of chef/mixin/recipe_definition_dsl_core. Load the file to activate the deprecation code. require 'chef/mixin/recipe_definition_dsl_core' chef-12.3.0/lib/chef/dsl/powershell.rb0000644000004100000410000000164512520074675017516 0ustar www-datawww-data# # Author:: Jay Mundrawala () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/ps_credential' class Chef module DSL module Powershell def ps_credential(username='placeholder', password) Chef::Util::Powershell::PSCredential.new(username, password) end end end end chef-12.3.0/lib/chef/dsl/registry_helper.rb0000644000004100000410000000456012520074675020540 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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. # # # Helper functions to access the windows registry from within recipes and # the not_if/only_if blocks in resources. This only exposes the methods # in the chef/win32/registry class which are reasonably side-effect-free. # The actual modification of the registry should be done via the registry_key # resource in a more idempotent way. # # class Chef module DSL module RegistryHelper # the registry instance is cheap to build and throwing it away ensures we # don't carry any state (e.g. magic 32-bit/64-bit settings) between calls def registry_key_exists?(key_path, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.key_exists?(key_path) end def registry_get_values(key_path, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.get_values(key_path) end def registry_has_subkeys?(key_path, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.has_subkeys?(key_path) end def registry_get_subkeys(key_path, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.get_subkeys(key_path) end def registry_value_exists?(key_path, value, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.value_exists?(key_path, value) end def registry_data_exists?(key_path, value, architecture = :machine) registry = Chef::Win32::Registry.new(run_context, architecture) registry.data_exists?(key_path, value) end end end end chef-12.3.0/lib/chef/dsl/include_recipe.rb0000644000004100000410000000257112520074675020303 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 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/log' class Chef module DSL module IncludeRecipe def include_recipe(*recipe_names) run_context.include_recipe(*recipe_names, current_cookbook: cookbook_name) end def load_recipe(recipe_name) run_context.load_recipe(recipe_name, current_cookbook: cookbook_name) end def require_recipe(*args) Chef::Log.warn("require_recipe is deprecated and will be removed in a future release, please use include_recipe") include_recipe(*args) end end end end # **DEPRECATED** # This used to be part of chef/mixin/language_include_recipe. Load the file to activate the deprecation code. require 'chef/mixin/language_include_recipe' chef-12.3.0/lib/chef/chef_class.rb0000644000004100000410000001166512520074675016645 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # NOTE: This class is not intended for internal use by the chef-client code. Classes in # chef-client should still have objects like the node and run_context injected into them # via their initializers. This class is still global state and will complicate writing # unit tests for internal Chef objects. It is intended to be used only by recipe code. # NOTE: When adding require lines here you are creating tight coupling on a global that may be # included in many different situations and ultimately that ends in tears with circular requires. # Note the way that the run_context, provider_priority_map and resource_priority_map are "dependency # injected" into this class by other objects and do not reference the class symbols in those files # directly and we do not need to require those files here. class Chef class << self # # Public API # # Get the node object # # @return [Chef::Node] node object of the chef-client run attr_reader :node # Get the run context # # @return [Chef::RunContext] run_context of the chef-client run attr_reader :run_context # Get the array of providers associated with a resource_name for the current node # # @param resource_name [Symbol] name of the resource as a symbol # @return [Array] Priority Array of Provider Classes to use for the resource_name on the node def get_provider_priority_array(resource_name) @provider_priority_map.get_priority_array(node, resource_name).dup end # Get the array of resources associated with a resource_name for the current node # # @param resource_name [Symbol] name of the resource as a symbol # @return [Array] Priority Array of Resource Classes to use for the resource_name on the node def get_resource_priority_array(resource_name) @resource_priority_map.get_priority_array(node, resource_name).dup end # Set the array of providers associated with a resource_name for the current node # # @param resource_name [Symbol] name of the resource as a symbol # @param priority_array [Array] Array of Classes to set as the priority for resource_name on the node # @param filter [Hash] Chef::Nodearray-style filter # @return [Array] Modified Priority Array of Provider Classes to use for the resource_name on the node def set_provider_priority_array(resource_name, priority_array, *filter) @provider_priority_map.set_priority_array(resource_name, priority_array, *filter).dup end # Get the array of resources associated with a resource_name for the current node # # @param resource_name [Symbol] name of the resource as a symbol # @param priority_array [Array] Array of Classes to set as the priority for resource_name on the node # @param filter [Hash] Chef::Nodearray-style filter # @return [Array] Modified Priority Array of Resource Classes to use for the resource_name on the node def set_resource_priority_array(resource_name, priority_array, *filter) @resource_priority_map.set_priority_array(resource_name, priority_array, *filter).dup end # # Dependency Injection API (Private not Public) # [ in the ruby sense these have to be public methods, but they are # *NOT* for public consumption ] # # Sets the resource_priority_map # # @api private # @param resource_priority_map [Chef::Platform::ResourcePriorityMap] def set_resource_priority_map(resource_priority_map) @resource_priority_map = resource_priority_map end # Sets the provider_priority_map # # @api private # @param provider_priority_map [Chef::Platform::providerPriorityMap] def set_provider_priority_map(provider_priority_map) @provider_priority_map = provider_priority_map end # Sets the node object # # @api private # @param node [Chef::Node] def set_node(node) @node = node end # Sets the run_context object # # @api private # @param run_context [Chef::RunContext] def set_run_context(run_context) @run_context = run_context end # Resets the internal state # # @api private def reset! @run_context = nil @node = nil @provider_priority_map = nil @resource_priority_map = nil end end end chef-12.3.0/lib/chef/platform.rb0000644000004100000410000000174512520074675016375 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # # Order of these headers is important: query helpers is needed by many things require 'chef/platform/query_helpers' require 'chef/platform/provider_mapping' class Chef class Platform # Functionality for this class is defined in chef/platform/provider_mapping # and chef/platform/query_helpers end end chef-12.3.0/lib/chef/platform/0000755000004100000410000000000012520074675016041 5ustar www-datawww-datachef-12.3.0/lib/chef/platform/resource_priority_map.rb0000644000004100000410000000140712520074675023015 0ustar www-datawww-dataclass Chef class Platform class ResourcePriorityMap include Singleton def initialize load_default_map end def get_priority_array(node, resource_name) priority_map.get(node, resource_name.to_sym) end def set_priority_array(resource_name, priority_array, *filter) priority resource_name.to_sym, priority_array.to_a, *filter end def priority(*args) priority_map.set(*args) end private def load_default_map require 'chef/resources' # MacOSX priority :package, Chef::Resource::HomebrewPackage, os: "darwin" end def priority_map require 'chef/node_map' @priority_map ||= Chef::NodeMap.new end end end end chef-12.3.0/lib/chef/platform/provider_priority_map.rb0000644000004100000410000000463712520074675023030 0ustar www-datawww-data class Chef class Platform class ProviderPriorityMap include Singleton def initialize load_default_map end def get_priority_array(node, resource_name) priority_map.get(node, resource_name.to_sym) end def set_priority_array(resource_name, priority_array, *filter) priority(resource_name.to_sym, priority_array.to_a, *filter) end def priority(*args) priority_map.set(*args) end private def load_default_map require 'chef/providers' # # Linux # # default block for linux O/Sen must come before platform_family exceptions priority :service, [ Chef::Provider::Service::Systemd, Chef::Provider::Service::Insserv, Chef::Provider::Service::Redhat, ], os: "linux" priority :service, [ Chef::Provider::Service::Systemd, Chef::Provider::Service::Arch, ], platform_family: "arch" priority :service, [ Chef::Provider::Service::Systemd, Chef::Provider::Service::Gentoo, ], platform_family: "gentoo" priority :service, [ # we can determine what systemd supports accurately Chef::Provider::Service::Systemd, # on debian-ish system if an upstart script exists that must win over sysv types Chef::Provider::Service::Upstart, Chef::Provider::Service::Insserv, Chef::Provider::Service::Debian, Chef::Provider::Service::Invokercd, ], platform_family: "debian" priority :service, [ Chef::Provider::Service::Systemd, Chef::Provider::Service::Insserv, Chef::Provider::Service::Redhat, ], platform_family: [ "rhel", "fedora", "suse" ] # # BSDen # priority :service, Chef::Provider::Service::Freebsd, os: [ "freebsd", "netbsd" ] priority :service, Chef::Provider::Service::Openbsd, os: [ "openbsd" ] # # Solaris-en # priority :service, Chef::Provider::Service::Solaris, os: "solaris2" # # Mac # priority :service, Chef::Provider::Service::Macosx, os: "darwin" priority :package, Chef::Provider::Package::Homebrew, os: "darwin" end def priority_map require 'chef/node_map' @priority_map ||= Chef::NodeMap.new end end end end chef-12.3.0/lib/chef/platform/provider_mapping.rb0000644000004100000410000004105412520074675021737 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/log' require 'chef/exceptions' require 'chef/mixin/params_validate' require 'chef/version_constraint/platform' # This file depends on nearly every provider in chef, but requiring them # directly causes circular requires resulting in uninitialized constant errors. # Therefore, we do the includes inline rather than up top. require 'chef/provider' class Chef class Platform class << self attr_writer :platforms def platforms @platforms ||= begin require 'chef/providers' { :freebsd => { :default => { :group => Chef::Provider::Group::Pw, :user => Chef::Provider::User::Pw, } }, :ubuntu => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Debian, }, ">= 11.10" => { :ifconfig => Chef::Provider::Ifconfig::Debian } # Chef::Provider::Service::Upstart is a candidate to be used in # ubuntu versions >= 13.10 but it currently requires all the # services to have an entry under /etc/init. We need to update it # to use the service ctl apis in order to migrate to using it on # ubuntu >= 13.10. }, :gcel => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Debian, } }, :linaro => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Debian, } }, :raspbian => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Debian, } }, :linuxmint => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Upstart, } }, :debian => { :default => { :package => Chef::Provider::Package::Apt, :service => Chef::Provider::Service::Debian, }, ">= 6.0" => { :service => Chef::Provider::Service::Insserv }, ">= 7.0" => { :ifconfig => Chef::Provider::Ifconfig::Debian } }, :xenserver => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, } }, :xcp => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, } }, :centos => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat }, "< 7" => { :service => Chef::Provider::Service::Redhat } }, :amazon => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, } }, :scientific => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Yum, }, "< 7" => { :service => Chef::Provider::Service::Redhat } }, :fedora => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat }, "< 15" => { :service => Chef::Provider::Service::Redhat } }, :opensuse => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Zypper, :group => Chef::Provider::Group::Suse }, # Only OpenSuSE 12.3+ should use the Usermod group provider: ">= 12.3" => { :group => Chef::Provider::Group::Usermod } }, :suse => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Zypper, :group => Chef::Provider::Group::Gpasswd }, "< 12.0" => { :group => Chef::Provider::Group::Suse, :service => Chef::Provider::Service::Redhat } }, :oracle => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Yum, }, "< 7" => { :service => Chef::Provider::Service::Redhat } }, :redhat => { :default => { :service => Chef::Provider::Service::Systemd, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat }, "< 7" => { :service => Chef::Provider::Service::Redhat } }, :ibm_powerkvm => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat } }, :cloudlinux => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat } }, :parallels => { :default => { :service => Chef::Provider::Service::Redhat, :package => Chef::Provider::Package::Yum, :ifconfig => Chef::Provider::Ifconfig::Redhat } }, :gentoo => { :default => { :package => Chef::Provider::Package::Portage, :service => Chef::Provider::Service::Gentoo, } }, :arch => { :default => { :package => Chef::Provider::Package::Pacman, :service => Chef::Provider::Service::Systemd, } }, :solaris => {}, :openindiana => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Ips, :group => Chef::Provider::Group::Usermod } }, :opensolaris => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Ips, :group => Chef::Provider::Group::Usermod } }, :nexentacore => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Solaris, :group => Chef::Provider::Group::Usermod } }, :omnios => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Ips, :group => Chef::Provider::Group::Usermod, :user => Chef::Provider::User::Solaris, } }, :solaris2 => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Ips, :group => Chef::Provider::Group::Usermod, :user => Chef::Provider::User::Solaris, }, "< 5.11" => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::Solaris, :group => Chef::Provider::Group::Usermod, :user => Chef::Provider::User::Solaris, } }, :smartos => { :default => { :mount => Chef::Provider::Mount::Solaris, :package => Chef::Provider::Package::SmartOS, :group => Chef::Provider::Group::Usermod } }, :hpux => { :default => { :group => Chef::Provider::Group::Usermod } }, :aix => { :default => { :group => Chef::Provider::Group::Aix, :mount => Chef::Provider::Mount::Aix, :ifconfig => Chef::Provider::Ifconfig::Aix, :package => Chef::Provider::Package::Aix, :user => Chef::Provider::User::Aix, :service => Chef::Provider::Service::Aix } }, :exherbo => { :default => { :package => Chef::Provider::Package::Paludis, :service => Chef::Provider::Service::Systemd, } }, :default => { :mount => Chef::Provider::Mount::Mount, :user => Chef::Provider::User::Useradd, :group => Chef::Provider::Group::Gpasswd, :ifconfig => Chef::Provider::Ifconfig, } } end end include Chef::Mixin::ParamsValidate def find(name, version) provider_map = platforms[:default].clone name_sym = name if name.kind_of?(String) name.downcase! name.gsub!(/\s/, "_") name_sym = name.to_sym end if platforms.has_key?(name_sym) platform_versions = platforms[name_sym].select {|k, v| k != :default } if platforms[name_sym].has_key?(:default) provider_map.merge!(platforms[name_sym][:default]) end platform_versions.each do |platform_version, provider| begin version_constraint = Chef::VersionConstraint::Platform.new(platform_version) if version_constraint.include?(version) Chef::Log.debug("Platform #{name.to_s} version #{version} found") provider_map.merge!(provider) end rescue Chef::Exceptions::InvalidPlatformVersion Chef::Log.debug("Chef::Version::Comparable does not know how to parse the platform version: #{version}") end end else Chef::Log.debug("Platform #{name} not found, using all defaults. (Unsupported platform?)") end provider_map end def find_platform_and_version(node) platform = nil version = nil if node[:platform] platform = node[:platform] elsif node.attribute?("os") platform = node[:os] end raise ArgumentError, "Cannot find a platform for #{node}" unless platform if node[:platform_version] version = node[:platform_version] elsif node[:os_version] version = node[:os_version] elsif node[:os_release] version = node[:os_release] end raise ArgumentError, "Cannot find a version for #{node}" unless version return platform, version end def provider_for_resource(resource, action=:nothing) node = resource.run_context && resource.run_context.node raise ArgumentError, "Cannot find the provider for a resource with no run context set" unless node provider = find_provider_for_node(node, resource).new(resource, resource.run_context) provider.action = action provider end def provider_for_node(node, resource_type) raise NotImplementedError, "#{self.class.name} no longer supports #provider_for_node" end def find_provider_for_node(node, resource_type) platform, version = find_platform_and_version(node) find_provider(platform, version, resource_type) end def set(args) validate( args, { :platform => { :kind_of => Symbol, :required => false, }, :version => { :kind_of => String, :required => false, }, :resource => { :kind_of => Symbol, }, :provider => { :kind_of => [ String, Symbol, Class ], } } ) if args.has_key?(:platform) if args.has_key?(:version) if platforms.has_key?(args[:platform]) if platforms[args[:platform]].has_key?(args[:version]) platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider] else platforms[args[:platform]][args[:version]] = { args[:resource].to_sym => args[:provider] } end else platforms[args[:platform]] = { args[:version] => { args[:resource].to_sym => args[:provider] } } end else if platforms.has_key?(args[:platform]) if platforms[args[:platform]].has_key?(:default) platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider] elsif args[:platform] == :default platforms[:default][args[:resource].to_sym] = args[:provider] else platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } } end else platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } } end end else if platforms.has_key?(:default) platforms[:default][args[:resource].to_sym] = args[:provider] else platforms[:default] = { args[:resource].to_sym => args[:provider] } end end end def find_provider(platform, version, resource_type) provider_klass = explicit_provider(platform, version, resource_type) || platform_provider(platform, version, resource_type) || resource_matching_provider(platform, version, resource_type) raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil? provider_klass end private def explicit_provider(platform, version, resource_type) resource_type.kind_of?(Chef::Resource) ? resource_type.provider : nil end def platform_provider(platform, version, resource_type) pmap = Chef::Platform.find(platform, version) rtkey = resource_type.kind_of?(Chef::Resource) ? resource_type.resource_name.to_sym : resource_type pmap.has_key?(rtkey) ? pmap[rtkey] : nil end def resource_matching_provider(platform, version, resource_type) if resource_type.kind_of?(Chef::Resource) begin Chef::Provider.const_get(resource_type.class.to_s.split('::').last) rescue NameError nil end else nil end end end end end chef-12.3.0/lib/chef/platform/service_helpers.rb0000644000004100000410000001112512520074675021550 0ustar www-datawww-data# # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # XXX: mixing shellout into a mixin into classes has to be code smell require 'chef/mixin/shell_out' require 'chef/mixin/which' class Chef class Platform class ServiceHelpers class << self include Chef::Mixin::ShellOut include Chef::Mixin::Which # This helper is mostly used to sort out the mess of different # linux mechanisms that can be used to start services. It does # not necessarily need to linux-specific, but currently all our # other service providers are narrowly platform-specific with no # alternatives. # # NOTE: if a system has (for example) chkconfig installed then we # should report that chkconfig is installed. The fact that a system # may also have systemd installed does not mean that we do not # report that systemd is also installed. This module is purely for # discovery of all the alternatives, handling the priority of the # different services is NOT a design concern of this module. # def service_resource_providers service_resource_providers = [] if ::File.exist?("/usr/sbin/update-rc.d") service_resource_providers << :debian end if ::File.exist?("/usr/sbin/invoke-rc.d") service_resource_providers << :invokercd end if ::File.exist?("/sbin/insserv") service_resource_providers << :insserv end # debian >= 6.0 has /etc/init but does not have upstart if ::File.exist?("/etc/init") && ::File.exist?("/sbin/start") service_resource_providers << :upstart end if ::File.exist?("/sbin/chkconfig") service_resource_providers << :redhat end if systemd_sanity_check? service_resource_providers << :systemd end service_resource_providers end def config_for_service(service_name) configs = [] if ::File.exist?("/etc/init.d/#{service_name}") configs << :initd end if ::File.exist?("/etc/init/#{service_name}.conf") configs << :upstart end if ::File.exist?("/etc/xinetd.d/#{service_name}") configs << :xinetd end if ::File.exist?("/etc/rc.d/#{service_name}") configs << :etc_rcd end if ::File.exist?("/usr/local/etc/rc.d/#{service_name}") configs << :usr_local_etc_rcd end if systemd_sanity_check? && platform_has_systemd_unit?(service_name) configs << :systemd end configs end private def systemctl_path if @systemctl_path.nil? @systemctl_path = which("systemctl") end @systemctl_path end def systemd_sanity_check? systemctl_path && File.exist?("/proc/1/comm") && File.open("/proc/1/comm").gets.chomp == "systemd" end def extract_systemd_services(command) output = shell_out!(command).stdout # first line finds e.g. "sshd.service" services = [] output.each_line do |line| fields = line.split services << fields[0] if fields[1] == "loaded" || fields[1] == "not-found" end # this splits off the suffix after the last dot to return "sshd" services += services.select {|s| s.match(/\.service$/) }.map { |s| s.sub(/(.*)\.service$/, '\1') } rescue Mixlib::ShellOut::ShellCommandFailed false end def platform_has_systemd_unit?(service_name) services = extract_systemd_services("#{systemctl_path} --all") + extract_systemd_services("#{systemctl_path} list-unit-files") services.include?(service_name) rescue Mixlib::ShellOut::ShellCommandFailed false end end end end end chef-12.3.0/lib/chef/platform/rebooter.rb0000644000004100000410000000325212520074675020211 0ustar www-datawww-data# # Author:: Chris Doherty ) # Copyright:: Copyright (c) 2014 Chef, 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/dsl/reboot_pending' require 'chef/log' require 'chef/platform' class Chef class Platform module Rebooter extend Chef::Mixin::ShellOut class << self def reboot!(node) reboot_info = node.run_context.reboot_info cmd = if Chef::Platform.windows? # should this do /f as well? do we then need a minimum delay to let apps quit? "shutdown /r /t #{reboot_info[:delay_mins]} /c \"#{reboot_info[:reason]}\"" else # probably Linux-only. "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\"" end Chef::Log.warn "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}" shell_out!(cmd) end # this is a wrapper function so Chef::Client only needs a single line of code. def reboot_if_needed!(node) if node.run_context.reboot_requested? reboot!(node) end end end end end end chef-12.3.0/lib/chef/platform/query_helpers.rb0000644000004100000410000000344412520074675021262 0ustar www-datawww-data# # Author:: Adam Jacob () # 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. # class Chef class Platform class << self def windows? if RUBY_PLATFORM =~ /mswin|mingw|windows/ true else false end end def windows_server_2003? return false unless windows? require 'wmi-lite/wmi' # CHEF-4888: Work around ruby #2618, expected to be fixed in Ruby 2.1.0 # https://github.com/ruby/ruby/commit/588504b20f5cc880ad51827b93e571e32446e5db # https://github.com/ruby/ruby/commit/27ed294c7134c0de582007af3c915a635a6506cd wmi = WmiLite::Wmi.new host = wmi.first_of('Win32_OperatingSystem') is_server_2003 = (host['version'] && host['version'].start_with?("5.2")) is_server_2003 end def supports_dsc?(node) node[:languages] && node[:languages][:powershell] && node[:languages][:powershell][:version].to_i >= 4 end def supports_dsc_invoke_resource?(node) require 'rubygems' supports_dsc?(node) && Gem::Version.new(node[:languages][:powershell][:version]) >= Gem::Version.new("5.0.10018.0") end end end end chef-12.3.0/lib/chef/policy_builder.rb0000644000004100000410000000257212520074675017555 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright 2008-2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/policy_builder/expand_node_object' require 'chef/policy_builder/policyfile' class Chef # PolicyBuilder contains classes that handles fetching policy from server or # disk and resolving any indirection (e.g. expanding run_list). # # INPUTS # * event stream object # * node object/run_list # * json_attribs # * override_runlist # # OUTPUTS # * mutated node object (implicit) # * a new RunStatus (probably doesn't need to be here) # * cookbooks sync'd to disk # * cookbook_hash is stored in run_context module PolicyBuilder def self.strategy if Chef::Config[:use_policyfile] Policyfile else ExpandNodeObject end end end end chef-12.3.0/lib/chef/json_compat.rb0000644000004100000410000001327612520074675017067 0ustar www-datawww-data# # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010 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. # Wrapper class for interacting with JSON. require 'ffi_yajl' require 'chef/exceptions' # We're requiring this to prevent breaking consumers using Hash.to_json require 'json' 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 CHEF_RESOURCESET = "Chef::ResourceCollection::ResourceSet".freeze CHEF_RESOURCELIST = "Chef::ResourceCollection::ResourceList".freeze class < e raise Chef::Exceptions::JSON::ParseError, e.message end end # Just call the JSON gem's parse method with a modified :max_nesting field def from_json(source, opts = {}) obj = parse(source, opts) # JSON gem requires top level object to be a Hash or Array (otherwise # you get the "must contain two octets" error). Yajl doesn't impose the # same limitation. For compatibility, we re-impose this condition. unless obj.kind_of?(Hash) or obj.kind_of?(Array) raise Chef::Exceptions::JSON::ParseError, "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) begin FFI_Yajl::Encoder.encode(obj, opts) rescue FFI_Yajl::EncodeError => e raise Chef::Exceptions::JSON::EncodeError, e.message end end def to_json_pretty(obj, opts = nil) opts ||= {} options_map = {} options_map[:pretty] = true options_map[:indent] = opts[:indent] if opts.has_key?(:indent) to_json(obj, options_map).chomp 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_RESOURCESET Chef::ResourceCollection::ResourceSet when CHEF_RESOURCELIST Chef::ResourceCollection::ResourceList when /^Chef::Resource/ Chef::Resource.find_descendants_by_name(json_class) else raise Chef::Exceptions::JSON::ParseError, "Unsupported `json_class` type '#{json_class}'" end end end end end chef-12.3.0/lib/chef/resource_definition_list.rb0000644000004100000410000000215112520074675021633 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/mixin/from_file' require 'chef/resource_definition' class Chef class ResourceDefinitionList include Chef::Mixin::FromFile attr_accessor :defines def initialize @defines = Hash.new end def define(resource_name, prototype_params=nil, &block) @defines[resource_name] = ResourceDefinition.new @defines[resource_name].define(resource_name, prototype_params, &block) true end end end chef-12.3.0/lib/chef/tasks/0000755000004100000410000000000012520074675015342 5ustar www-datawww-datachef-12.3.0/lib/chef/tasks/chef_repo.rake0000644000004100000410000001512412520074675020143 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc. # Copyright:: Copyright (c) 2014, Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TOPDIR = '.' require 'rake' desc "By default, print deprecation notice" task :default do puts deprecation_notice end desc "Install the latest copy of the repository on this Chef Server" task :install do puts deprecation_notice puts 'The `install` rake task, which included the `update`, `roles`, and' puts '`upload_cookbooks` rake tasks is replaced by the `knife upload`' puts 'sub-command. The notion of "installing" the chef-repo to the Chef' puts 'Server. Previously the `install` task would manage server and' puts 'client configuration. This will not work at all on Chef Server 11+' puts 'and client configuration should be managed with the `chef-client`' puts 'cookbook.' end desc "Update your repository from source control" task :update do puts deprecation_notice puts 'The `update` rake task previously updated the chef-repo from' puts 'the detected version control system, either svn or git. However,' puts 'it has not been recommended for users for years. Most users in' puts 'the community use `git`, so the Subversion functionality is not' puts 'required, and `git pull` is sufficient for many workflows. The' puts 'world of git workflows is rather different now than it was when' puts 'this rake task was created.' end desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)" task :new_cookbook do cb = ENV['COOKBOOK'] || 'my_cookbook_name' puts deprecation_notice puts 'The `new_cookbook` rake task is replaced by the ChefDK cookbook' puts 'generator. To generate a new cookbook run:' puts puts "chef generate cookbook #{ENV['COOKBOOK']}" puts puts 'Or, if you are not using ChefDK, use `knife cookbook create`:' puts puts "knife cookbook create #{ENV['COOKBOOK']}" end desc "Create a new self-signed SSL certificate for FQDN=foo.example.com" task :ssl_cert do puts deprecation_notice puts 'The `ssl_cert` rake task is superseded by using the CHEF-maintained' puts '`openssl` cookbook\'s `openssl_x509` resource which can generate' puts 'self-signed certificate chains as convergent resources.' puts puts 'https://supermarket.getchef.com/cookbooks/openssl' end desc "Build cookbook metadata.json from metadata.rb" task :metadata do puts deprecation_notice puts 'The `metadata` rake task is not recommended. Cookbook' puts '`metadata.json` is automatically generated from `metadata.rb`' puts 'by `knife` when uploading cookbooks to the Chef Server.' end desc "Update roles" task :roles do puts deprecation_notice puts 'The `roles` rake task is not recommended. If you are using Ruby' puts 'role files (roles/*.rb), you can upload them all with:' puts puts 'knife role from file roles/*' puts puts 'If you are using JSON role files (roles/*.json), you can upload' puts 'them all with:' puts puts 'knife upload roles/*.json' end desc "Update a specific role" task :role do puts deprecation_notice puts 'The `role` rake task is not recommended. If you are using Ruby' puts 'role files, you can upload a single role with:' puts puts 'knife role from file rolename.rb' puts puts 'If you are using JSON role files, you can upload a single role with' puts puts 'knife upload roles/rolename.json' end desc "Upload all cookbooks" task :upload_cookbooks do puts deprecation_notice puts deprecated_cookbook_upload end desc "Upload a single cookbook" task :upload_cookbook do puts deprecation_notice puts deprecated_cookbook_upload end desc "Test all cookbooks" task :test_cookbooks do puts deprecation_notice puts 'The `test_cookbooks` rake task is no longer recommended. Previously' puts 'it only performed a syntax check, and did no other kind of testing,' puts 'and the Chef Community has a rich ecosystem of testing tools for' puts 'various purposes:' puts puts '- knife cookbook test will perform a syntax check, as this task did' puts ' before.' puts '- rubocop and foodcritic will perform lint checking for Ruby and' puts ' Chef cookbook style according to community standards.' puts '- ChefSpec will perform unit testing' puts '- Test Kitchen will perform convergence and post-convergence' puts ' testing on virtual machines.' end desc "Test a single cookbook" task :test_cookbook => [:test_cookbooks] namespace :databag do desc "Upload a single databag" task :upload do puts deprecation_notice puts 'The `data_bags:upload` task is not recommended. You should use' puts 'the `knife upload` sub-command for uploading data bag items.' puts puts 'knife upload data_bags/bagname/itemname.json' end desc "Upload all databags" task :upload_all do puts deprecation_notice puts 'The `data_bags:upload_all` task is not recommended. You should' puts 'use the `knife upload` sub-command for uploading data bag items.' puts puts 'knife upload data_bags/*' end desc "Create a databag" task :create do puts deprecation_notice puts deprecated_data_bag_creation end desc "Create a databag item stub" task :create_item do puts deprecation_notice puts deprecated_data_bag_creation end end def deprecation_notice %Q[************************************************* NOTICE: Chef Repository Rake Tasks Are Deprecated ************************************************* ] end def deprecated_cookbook_upload %Q[ The `upload_cookbook` and `upload_cookbooks` rake tasks are not recommended. These tasks are replaced by other, better workflow tools, such as `knife cookbook upload`, `knife upload`, or `berks` ] end def deprecated_data_bag_creation %Q[ The `data_bags:create` and `data_bags:create_item` tasks are not recommended. You should create data bag items as JSON files in the data_bags directory, with a sub-directory for each bag, and use `knife upload` to upload them. For example, if you have a data bags named `users`, with `finn`, and `jake` items, you would have: ./data_bags/users/finn.json ./data-bags/users/jake.json ] end chef-12.3.0/lib/chef/resource.rb0000644000004100000410000011367012520074675016401 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Walters () # 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 'chef/mixin/params_validate' require 'chef/dsl/platform_introspection' require 'chef/dsl/data_query' require 'chef/dsl/registry_helper' require 'chef/dsl/reboot_pending' require 'chef/mixin/convert_to_class_name' require 'chef/guard_interpreter/resource_guard_interpreter' require 'chef/resource/conditional' require 'chef/resource/conditional_action_not_nothing' require 'chef/resource_collection' require 'chef/node_map' require 'chef/node' require 'chef/platform' require 'chef/resource/resource_notification' require 'chef/mixin/deprecation' require 'chef/mixin/provides' class Chef class Resource # # Generic User DSL (not resource-specific) # include Chef::DSL::DataQuery include Chef::DSL::PlatformIntrospection include Chef::DSL::RegistryHelper include Chef::DSL::RebootPending extend Chef::Mixin::Provides # # The node the current Chef run is using. # # Corresponds to `run_context.node`. # # @return [Chef::Node] The node the current Chef run is using. # def node run_context && run_context.node end # # Find existing resources by searching the list of existing resources. Possible # forms are: # # find(:file => "foobar") # find(:file => [ "foobar", "baz" ]) # find("file[foobar]", "file[baz]") # find("file[foobar,baz]") # # Calls `run_context.resource_collection.find(*args)` # # @return the matching resource, or an Array of matching resources. # # @raise ArgumentError if you feed it bad lookup information # @raise RuntimeError if it can't find the resources you are looking for. # def resources(*args) run_context.resource_collection.find(*args) end # # Resource User Interface (for users) # # # Create a new Resource. # # @param name The name of this resource (corresponds to the #name attribute, # used for notifications to this resource). # @param run_context The context of the Chef run. Corresponds to #run_context. # def initialize(name, run_context=nil) name(name) @run_context = run_context @noop = nil @before = nil @params = Hash.new @provider = nil @allowed_actions = [ :nothing ] @action = :nothing @updated = false @updated_by_last_action = false @supports = {} @ignore_failure = false @retries = 0 @retry_delay = 2 @not_if = [] @only_if = [] @source_line = nil # We would like to raise an error when the user gives us a guard # interpreter and a ruby_block to the guard. In order to achieve this # we need to understand when the user overrides the default guard # interpreter. Therefore we store the default separately in a different # attribute. @guard_interpreter = nil @default_guard_interpreter = :default @elapsed_time = 0 @sensitive = false end # # The name of this particular resource. # # This special resource attribute is set automatically from the declaration # of the resource, e.g. # # execute 'Vitruvius' do # command 'ls' # end # # Will set the name to "Vitruvius". # # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`. # # This is also used for resource notifications and subscribes in the same manner. # # This will coerce any object into a string via #to_s. Arrays are a special case # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more # awkward `package[["foo", "bar"]]` that #to_s would produce. # # @param name [Object] The name to set, typically a String or Array # @return [String] The name of this Resource. # def name(name=nil) if !name.nil? if name.is_a?(Array) @name = name.join(', ') else @name = name.to_s end end @name end # # The action or actions that will be taken when this resource is run. # # @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`) # @return [Array[Symbol]] the list of actions. # def action(arg=nil) if arg action_list = arg.kind_of?(Array) ? arg : [ arg ] action_list = action_list.collect { |a| a.to_sym } action_list.each do |action| validate( { action: action }, { action: { kind_of: Symbol, equal_to: @allowed_actions } } ) end @action = action_list else @action end end # # Sets up a notification that will run a particular action on another resource # if and when *this* resource is updated by an action. # # If the action does nothing--does not update this resource, the # notification never triggers.) # # Only one resource may be specified per notification. # # `delayed` notifications will only *ever* happen once per resource, so if # multiple resources all notify a single resource to perform the same action, # the action will only happen once. However, if they ask for different # actions, each action will happen once, in the order they were updated. # # `immediate` notifications will cause the action to be triggered once per # notification, regardless of how many other resources have triggered the # notification as well. # # @param action The action to run on the other resource. # @param resource_spec [String, Hash, Chef::Resource] The resource to run. # @param timing [String, Symbol] When to notify. Has these values: # - `delayed`: Will run the action on the other resource after all other # actions have been run. This is the default. # - `immediate`, `immediately`: Will run the action on the other resource # immediately (before any other action is run). # # @example Resource by string # file '/foo.txt' do # content 'hi' # notifies :create, 'file[/bar.txt]' # end # file '/bar.txt' do # action :nothing # content 'hi' # end # @example Resource by hash # file '/foo.txt' do # content 'hi' # notifies :create, file: '/bar.txt' # end # file '/bar.txt' do # action :nothing # content 'hi' # end # @example Direct Resource # bar = file '/bar.txt' do # action :nothing # content 'hi' # end # file '/foo.txt' do # content 'hi' # notifies :create, bar # end # def notifies(action, resource_spec, timing=:delayed) # when using old-style resources(:template => "/foo.txt") style, you # could end up with multiple resources. validate_resource_spec!(resource_spec) resources = [ resource_spec ].flatten resources.each do |resource| case timing.to_s when 'delayed' notifies_delayed(action, resource) when 'immediate', 'immediately' notifies_immediately(action, resource) else raise ArgumentError, "invalid timing: #{timing} for notifies(#{action}, #{resources.inspect}, #{timing}) resource #{self} "\ "Valid timings are: :delayed, :immediate, :immediately" end end true end # # Subscribes to updates from other resources, causing a particular action to # run on *this* resource when the other resource is updated. # # If multiple resources are specified, this resource action will be run if # *any* of them change. # # This notification will only trigger *once*, no matter how many other # resources are updated (or how many actions are run by a particular resource). # # @param action The action to run on the other resource. # @param resources [String, Resource, Array[String, Resource]] The resources to subscribe to. # @param timing [String, Symbol] When to notify. Has these values: # - `delayed`: An update will cause the action to run after all other # actions have been run. This is the default. # - `immediate`, `immediately`: The action will run immediately following # the other resource being updated. # # @example Resources by string # file '/foo.txt' do # content 'hi' # action :nothing # subscribes :create, 'file[/bar.txt]' # end # file '/bar.txt' do # content 'hi' # end # @example Direct resource # bar = file '/bar.txt' do # content 'hi' # end # file '/foo.txt' do # content 'hi' # action :nothing # subscribes :create, '/bar.txt' # end # @example Multiple resources by string # file '/foo.txt' do # content 'hi' # action :nothing # subscribes :create, [ 'file[/bar.txt]', 'file[/baz.txt]' ] # end # file '/bar.txt' do # content 'hi' # end # file '/baz.txt' do # content 'hi' # end # @example Multiple resources # bar = file '/bar.txt' do # content 'hi' # end # baz = file '/bar.txt' do # content 'hi' # end # file '/foo.txt' do # content 'hi' # action :nothing # subscribes :create, [ bar, baz ] # end # def subscribes(action, resources, timing=:delayed) resources = [resources].flatten resources.each do |resource| if resource.is_a?(String) resource = Chef::Resource.new(resource, run_context) end if resource.run_context.nil? resource.run_context = run_context end resource.notifies(action, self, timing) end true end # # A command or block that indicates whether to actually run this resource. # The command or block is run just before the action actually executes, and # the action will be skipped if the block returns false. # # If a block is specified, it must return `true` in order for the Resource # to be executed. # # If a command is specified, the resource's #guard_interpreter will run the # command and interpret the results according to `opts`. For example, the # default `execute` resource will be treated as `false` if the command # returns a non-zero exit code, and `true` if it returns 0. Thus, in the # default case: # # - `only_if "your command"` will perform the action only if `your command` # returns 0. # - `only_if "your command", valid_exit_codes: [ 1, 2, 3 ]` will perform the # action only if `your command` returns 1, 2, or 3. # # @param command [String] A string to execute. # @param opts [Hash] Options control the execution of the command # @param block [Proc] A ruby block to run. Ignored if a command is given. # def only_if(command=nil, opts={}, &block) if command || block_given? @only_if << Conditional.only_if(self, command, opts, &block) end @only_if end # # A command or block that indicates whether to actually run this resource. # The command or block is run just before the action actually executes, and # the action will be skipped if the block returns true. # # If a block is specified, it must return `false` in order for the Resource # to be executed. # # If a command is specified, the resource's #guard_interpreter will run the # command and interpret the results according to `opts`. For example, the # default `execute` resource will be treated as `false` if the command # returns a non-zero exit code, and `true` if it returns 0. Thus, in the # default case: # # - `not_if "your command"` will perform the action only if `your command` # returns a non-zero code. # - `only_if "your command", valid_exit_codes: [ 1, 2, 3 ]` will perform the # action only if `your command` returns something other than 1, 2, or 3. # # @param command [String] A string to execute. # @param opts [Hash] Options control the execution of the command # @param block [Proc] A ruby block to run. Ignored if a command is given. # def not_if(command=nil, opts={}, &block) if command || block_given? @not_if << Conditional.not_if(self, command, opts, &block) end @not_if end # # The number of times to retry this resource if it fails by throwing an # exception while running an action. Default: 0 # # When the retries have run out, the Resource will throw the last # exception. # # @param arg [Integer] The number of retries. # @return [Integer] The number of retries. # def retries(arg=nil) set_or_return(:retries, arg, kind_of: Integer) end attr_writer :retries # # The number of seconds to wait between retries. Default: 2. # # @param arg [Integer] The number of seconds to wait between retries. # @return [Integer] The number of seconds to wait between retries. # def retry_delay(arg=nil) set_or_return(:retry_delay, arg, kind_of: Integer) end attr_writer :retry_delay # # Whether to treat this resource's data as sensitive. If set, no resource # data will be displayed in log output. # # @param arg [Boolean] Whether this resource is sensitive or not. # @return [Boolean] Whether this resource is sensitive or not. # def sensitive(arg=nil) set_or_return(:sensitive, arg, :kind_of => [ TrueClass, FalseClass ]) end attr_writer :sensitive # ??? TODO unreferenced. Delete? attr_reader :not_if_args # ??? TODO unreferenced. Delete? attr_reader :only_if_args # # The time it took (in seconds) to run the most recently-run action. Not # cumulative across actions. This is set to 0 as soon as a new action starts # running, and set to the elapsed time at the end of the action. # # @return [Integer] The time (in seconds) it took to process the most recent # action. Not cumulative. # attr_reader :elapsed_time # # The guard interpreter that will be used to process `only_if` and `not_if` # statements. If left unset, the #default_guard_interpreter will be used. # # Must be a resource class like `Chef::Resource::Execute`, or # a corresponding to the name of a resource. The resource must descend from # `Chef::Resource::Execute`. # # TODO this needs to be coerced on input so that retrieval is consistent. # # @param arg [Class, Symbol, String] The Guard interpreter resource class/ # symbol/name. # @return [Class, Symbol, String] The Guard interpreter resource. # def guard_interpreter(arg=nil) if arg.nil? @guard_interpreter || @default_guard_interpreter else set_or_return(:guard_interpreter, arg, :kind_of => Symbol) end end # # Get the value of the state attributes in this resource as a hash. # # @return [Hash{Symbol => Object}] A Hash of attribute => value for the # Resource class's `state_attrs`. def state self.class.state_attrs.inject({}) do |state_attrs, attr_name| state_attrs[attr_name] = send(attr_name) state_attrs end end # # The value of the identity attribute, if declared. Falls back to #name if # no identity attribute is declared. # # @return The value of the identity attribute. # def identity if identity_attr = self.class.identity_attr send(identity_attr) else name end end # # Whether to ignore failures. If set to `true`, and this resource when an # action is run, the resource will be marked as failed but no exception will # be thrown (and no error will be output). Defaults to `false`. # # TODO ignore_failure and retries seem to be mutually exclusive; I doubt # that was intended. # # @param arg [Boolean] Whether to ignore failures. # @return Whether this resource will ignore failures. # def ignore_failure(arg=nil) set_or_return(:ignore_failure, arg, kind_of: [ TrueClass, FalseClass ]) end attr_writer :ignore_failure # # Equivalent to #ignore_failure. # def epic_fail(arg=nil) ignore_failure(arg) end # # Make this resource into an exact (shallow) copy of the other resource. # # @param resource [Chef::Resource] The resource to copy from. # def load_from(resource) resource.instance_variables.each do |iv| unless iv == :@source_line || iv == :@action || iv == :@not_if || iv == :@only_if self.instance_variable_set(iv, resource.instance_variable_get(iv)) end end end # # Runs the given action on this resource, immediately. # # @param action The action to run (e.g. `:create`) # @param notification_type The notification type that triggered this (if any) # @param notifying_resource The resource that triggered this notification (if any) # # @raise Any error that occurs during the actual action. # def run_action(action, notification_type=nil, notifying_resource=nil) # reset state in case of multiple actions on the same resource. @elapsed_time = 0 start_time = Time.now events.resource_action_start(self, action, notification_type, notifying_resource) # Try to resolve lazy/forward references in notifications again to handle # the case where the resource was defined lazily (ie. in a ruby_block) resolve_notification_references validate_action(action) if Chef::Config[:verbose_logging] || Chef::Log.level == :debug # This can be noisy Chef::Log.info("Processing #{self} action #{action} (#{defined_at})") end # ensure that we don't leave @updated_by_last_action set to true # on accident updated_by_last_action(false) # Don't modify @retries directly and keep it intact, so that the # recipe_snippet from ResourceFailureInspector can print the value # that was set in the resource block initially. remaining_retries = retries begin return if should_skip?(action) provider_for_action(action).run_action rescue Exception => e if ignore_failure Chef::Log.error("#{custom_exception_message(e)}; ignore_failure is set, continuing") events.resource_failed(self, action, e) elsif remaining_retries > 0 events.resource_failed_retriable(self, action, remaining_retries, e) remaining_retries -= 1 Chef::Log.info("Retrying execution of #{self}, #{remaining_retries} attempt(s) left") sleep retry_delay retry else events.resource_failed(self, action, e) raise customize_exception(e) end ensure @elapsed_time = Time.now - start_time # Reporting endpoint doesn't accept a negative resource duration so set it to 0. # A negative value can occur when a resource changes the system time backwards @elapsed_time = 0 if @elapsed_time < 0 events.resource_completed(self) end end # # Generic Ruby and Data Structure Stuff (for user) # def to_s "#{@resource_name}[#{@name}]" end def to_text return "suppressed sensitive resource output" if sensitive ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS text = "# Declared in #{@source_line}\n\n" text << self.class.dsl_name + "(\"#{name}\") do\n" ivars.each do |ivar| if (value = instance_variable_get(ivar)) && !(value.respond_to?(:empty?) && value.empty?) value_string = value.respond_to?(:to_text) ? value.to_text : value.inspect text << " #{ivar.to_s.sub(/^@/,'')} #{value_string}\n" end end [@not_if, @only_if].flatten.each do |conditional| text << " #{conditional.to_text}\n" end text << "end\n" end def inspect ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS ivars.inject("<#{to_s}") do |str, ivar| str << " #{ivar}: #{instance_variable_get(ivar).inspect}" end << ">" end # as_json does most of the to_json heavy lifted. It exists here in case activesupport # is loaded. activesupport will call as_json and skip over to_json. This ensure # json is encoded as expected def as_json(*a) safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS instance_vars = Hash.new safe_ivars.each do |iv| instance_vars[iv.to_s.sub(/^@/, '')] = instance_variable_get(iv) end { 'json_class' => self.class.name, 'instance_vars' => instance_vars } end # Serialize this object as a hash def to_json(*a) results = as_json Chef::JSONCompat.to_json(results, *a) end def to_hash safe_ivars = instance_variables.map { |ivar| ivar.to_sym } - FORBIDDEN_IVARS instance_vars = Hash.new safe_ivars.each do |iv| key = iv.to_s.sub(/^@/,'').to_sym instance_vars[key] = instance_variable_get(iv) end instance_vars end def self.json_create(o) resource = self.new(o["instance_vars"]["@name"]) o["instance_vars"].each do |k,v| resource.instance_variable_set("@#{k}".to_sym, v) end resource end # # Resource Definition Interface (for resource developers) # include Chef::Mixin::ParamsValidate include Chef::Mixin::Deprecation # # The provider class for this resource. # # If this is not set, `provider_for_action` will dynamically determine the # provider. # # @param arg [String, Symbol, Class] Sets the provider class for this resource. # If passed a String or Symbol, e.g. `:file` or `"file"`, looks up the # provider based on the name. # @return The provider class for this resource. # def provider(arg=nil) klass = if arg.kind_of?(String) || arg.kind_of?(Symbol) lookup_provider_constant(arg) else arg end set_or_return(:provider, klass, kind_of: [ Class ]) end def provider=(arg) provider(arg) 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 # attributes are attributes that could be populated by examining the state # of the system (e.g., File.stat can tell you the permissions on an # existing file). Contrarily, attributes that are not "state attributes" # usually modify the way Chef itself behaves, for example by providing # additional options for a package manager to use when installing a # package. # # This list is used by the Chef client auditing system to extract # information from resources to describe changes made to the system. def self.state_attrs(*attr_names) @state_attrs ||= [] @state_attrs = attr_names unless attr_names.empty? # Return *all* state_attrs that this class has, including inherited ones if superclass.respond_to?(:state_attrs) superclass.state_attrs + @state_attrs else @state_attrs end end # Set or return the "identity attribute" for this resource class. This is # generally going to be the "name attribute" for this resource. In other # words, the resource type plus this attribute uniquely identify a given # bit of state that chef manages. For a File resource, this would be the # path, for a package resource, it will be the package name. This will show # up in chef-client's audit records as a searchable field. def self.identity_attr(attr_name=nil) @identity_attr ||= nil @identity_attr = attr_name if attr_name # If this class doesn't have an identity attr, we'll defer to the superclass: if @identity_attr || !superclass.respond_to?(:identity_attr) @identity_attr else superclass.identity_attr end end # # The guard interpreter that will be used to process `only_if` and `not_if` # statements by default. If left unset, or set to `:default`, the guard # interpreter used will be Chef::GuardInterpreter::DefaultGuardInterpreter. # # Must be a resource class like `Chef::Resource::Execute`, or # a corresponding to the name of a resource. The resource must descend from # `Chef::Resource::Execute`. # # TODO this needs to be coerced on input so that retrieval is consistent. # # @return [Class, Symbol, String] the default Guard interpreter resource. # attr_reader :default_guard_interpreter # # The list of actions this Resource is allowed to have. Setting `action` # will fail unless it is in this list. Default: [ :nothing ] # # @return [Array] The list of actions this Resource is allowed to # have. # attr_accessor :allowed_actions # # Whether or not this resource was updated during an action. If multiple # actions are set on the resource, this will be `true` if *any* action # caused an update to happen. # # @return [Boolean] Whether the resource was updated during an action. # attr_reader :updated # # Whether or not this resource was updated during an action. If multiple # actions are set on the resource, this will be `true` if *any* action # caused an update to happen. # # @return [Boolean] Whether the resource was updated during an action. # def updated? updated end # # Whether or not this resource was updated during the most recent action. # This is set to `false` at the beginning of each action. # # @param true_or_false [Boolean] Whether the resource was updated during the # current / most recent action. # @return [Boolean] Whether the resource was updated during the most recent action. # def updated_by_last_action(true_or_false) @updated ||= true_or_false @updated_by_last_action = true_or_false end # # Whether or not this resource was updated during the most recent action. # This is set to `false` at the beginning of each action. # # @return [Boolean] Whether the resource was updated during the most recent action. # def updated_by_last_action? @updated_by_last_action end # # Set whether this class was updated during an action. # # @deprecated Multiple actions are supported by resources. Please call {}#updated_by_last_action} instead. # def updated=(true_or_false) Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.") Chef::Log.warn("Called from:") caller[0..3].each {|line| Chef::Log.warn(line)} updated_by_last_action(true_or_false) @updated = true_or_false end # # The DSL name of this resource (e.g. `package` or `yum_package`) # # @return [String] The DSL name of this resource. def self.dsl_name convert_to_snake_case(name, 'Chef::Resource') end # # The name of this resource (e.g. `file`) # # @return [String] The name of this resource. # attr_reader :resource_name # # Sets a list of capabilities of the real resource. For example, `:remount` # (for filesystems) and `:restart` (for services). # # TODO Calling resource.supports({}) will not set this to empty; it will do # a get instead. That's wrong. # # @param args Hash{Symbol=>Boolean} If non-empty, sets the capabilities of # this resource. Default: {} # @return Hash{Symbol=>Boolean} An array of things this resource supports. # def supports(args={}) if args.any? @supports = args else @supports end end def supports=(args) supports(args) end # # A hook called after a resource is created. Meant to be overriden by # subclasses. # def after_created nil end # # The module where Chef should look for providers for this resource. # The provider for `MyResource` will be looked up using # `provider_base::MyResource`. Defaults to `Chef::Provider`. # # @param arg [Module] The module containing providers for this resource # @return [Module] The module containing providers for this resource # # @example # class MyResource < Chef::Resource # provider_base Chef::Provider::Deploy # # ...other stuff # end # def self.provider_base(arg=nil) @provider_base ||= arg @provider_base ||= Chef::Provider end # # Internal Resource Interface (for Chef) # FORBIDDEN_IVARS = [:@run_context, :@not_if, :@only_if, :@enclosing_provider] HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider] include Chef::Mixin::ConvertToClassName extend Chef::Mixin::ConvertToClassName # XXX: this is required for definition params inside of the scope of a # subresource to work correctly. attr_accessor :params # @return [Chef::RunContext] The run context for this Resource. This is # where the context for the current Chef run is stored, including the node # and the resource collection. attr_accessor :run_context # @return [String] The cookbook this resource was declared in. attr_accessor :cookbook_name # @return [String] The recipe this resource was declared in. attr_accessor :recipe_name # @return [Chef::Provider] The provider this resource was declared in (if # it was declared in an LWRP). When you call methods that do not exist # on this Resource, Chef will try to call the method on the provider # as well before giving up. attr_accessor :enclosing_provider # @return [String] The source line where this resource was declared. # Expected to come from caller() or a stack trace, it usually follows one # of these formats: # /some/path/to/file.rb:80:in `wombat_tears' # C:/some/path/to/file.rb:80 in 1`wombat_tears' attr_accessor :source_line # @return [String] The actual name that was used to create this resource. # Sometimes, when you say something like `package 'blah'`, the system will # create a different resource (i.e. `YumPackage`). When this happens, the # user will expect to see the thing they wrote, not the type that was # returned. May be `nil`, in which case callers should read #resource_name. # See #declared_key. attr_accessor :declared_type # # Iterates over all immediate and delayed notifications, calling # resolve_resource_reference on each in turn, causing them to # resolve lazy/forward references. def resolve_notification_references run_context.immediate_notifications(self).each { |n| n.resolve_resource_reference(run_context.resource_collection) } run_context.delayed_notifications(self).each {|n| n.resolve_resource_reference(run_context.resource_collection) } end # Helper for #notifies def notifies_immediately(action, resource_spec) run_context.notifies_immediately(Notification.new(resource_spec, action, self)) end # Helper for #notifies def notifies_delayed(action, resource_spec) run_context.notifies_delayed(Notification.new(resource_spec, action, self)) end class << self # back-compat # NOTE: that we do not support unregistering classes as descendents like # we used to for LWRP unloading because that was horrible and removed in # Chef-12. alias :resource_classes :descendants alias :find_subclass_by_name :find_descendants_by_name end # If an unknown method is invoked, determine whether the enclosing Provider's # lexical scope can fulfill the request. E.g. This happens when the Resource's # block invokes new_resource. def method_missing(method_symbol, *args, &block) if enclosing_provider && enclosing_provider.respond_to?(method_symbol) enclosing_provider.send(method_symbol, *args, &block) else raise NoMethodError, "undefined method `#{method_symbol.to_s}' for #{self.class.to_s}" end end # Helper for #notifies def validate_resource_spec!(resource_spec) run_context.resource_collection.validate_lookup_spec!(resource_spec) end # We usually want to store and reference resources by their declared type and not the actual type that # was looked up by the Resolver (IE, "package" becomes YumPackage class). If we have not been provided # the declared key we want to fall back on the old to_s key. def declared_key return to_s if declared_type.nil? "#{declared_type}[#{@name}]" end def immediate_notifications run_context.immediate_notifications(self) end def delayed_notifications run_context.delayed_notifications(self) end def defined_at # The following regexp should match these two sourceline formats: # /some/path/to/file.rb:80:in `wombat_tears' # C:/some/path/to/file.rb:80 in 1`wombat_tears' # extracting the path to the source file and the line number. (file, line_no) = source_line.match(/(.*):(\d+):?.*$/).to_a[1,2] if source_line if cookbook_name && recipe_name && source_line "#{cookbook_name}::#{recipe_name} line #{line_no}" elsif source_line "#{file} line #{line_no}" else "dynamically defined" end end # # The cookbook in which this Resource was defined (if any). # # @return Chef::CookbookVersion The cookbook in which this Resource was defined. # def cookbook_version if cookbook_name run_context.cookbook_collection[cookbook_name] end end def events run_context.events end def validate_action(action) raise ArgumentError, "nil is not a valid action for resource #{self}" if action.nil? end def provider_for_action(action) require 'chef/provider_resolver' provider = Chef::ProviderResolver.new(node, self, action).resolve.new(self, run_context) provider.action = action provider end # ??? TODO Seems unused. Delete? def noop(tf=nil) if !tf.nil? raise ArgumentError, "noop must be true or false!" unless tf == true || tf == false @noop = tf end @noop end # TODO Seems unused. Delete? def is(*args) if args.size == 1 args.first else return *args end end # # Preface an exception message with generic Resource information. # # @param e [StandardError] An exception with `e.message` # @return [String] An exception message customized with class name. # def custom_exception_message(e) "#{self} (#{defined_at}) had an error: #{e.class.name}: #{e.message}" end def customize_exception(e) new_exception = e.exception(custom_exception_message(e)) new_exception.set_backtrace(e.backtrace) new_exception end # Evaluates not_if and only_if conditionals. Returns a falsey value if any # of the conditionals indicate that this resource should be skipped, i.e., # if an only_if evaluates to false or a not_if evaluates to true. # # If this resource should be skipped, returns the first conditional that # "fails" its check. Subsequent conditionals are not evaluated, so in # general it's not a good idea to rely on side effects from not_if or # only_if commands/blocks being evaluated. # # Also skips conditional checking when the action is :nothing def should_skip?(action) conditional_action = ConditionalActionNotNothing.new(action) conditionals = [ conditional_action ] + only_if + not_if conditionals.find do |conditional| if conditional.continue? false else events.resource_skipped(self, action, conditional) Chef::Log.debug("Skipping #{self} due to #{conditional.description}") true end end end # Returns a resource based on a short_name and node # # ==== Parameters # short_name:: short_name of the resource (ie :directory) # node:: Node object to look up platform and version in # # === Returns # :: returns the proper Chef::Resource class def self.resource_for_node(short_name, node) require 'chef/resource_resolver' klass = Chef::ResourceResolver.new(node, short_name).resolve raise Chef::Exceptions::NoSuchResourceType.new(short_name, node) if klass.nil? klass end # Returns the class of a Chef::Resource based on the short name # ==== Parameters # short_name:: short_name of the resource (ie :directory) # # === Returns # :: returns the proper Chef::Resource class def self.resource_matching_short_name(short_name) begin rname = convert_to_class_name(short_name.to_s) Chef::Resource.const_get(rname) rescue NameError nil end end private def lookup_provider_constant(name) begin self.class.provider_base.const_get(convert_to_class_name(name.to_s)) rescue NameError => e if e.to_s =~ /#{Regexp.escape(self.class.provider_base.to_s)}/ raise ArgumentError, "No provider found to match '#{name}'" else raise e end end end end end chef-12.3.0/lib/chef/runner.rb0000644000004100000410000001002412520074675016050 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2008, 2010 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 'chef/mixin/params_validate' require 'chef/node' require 'chef/resource_collection' class Chef # == Chef::Runner # This class is responsible for executing the steps in a Chef run. class Runner attr_reader :run_context attr_reader :delayed_actions include Chef::Mixin::ParamsValidate def initialize(run_context) @run_context = run_context @delayed_actions = [] end def events @run_context.events end # Determine the appropriate provider for the given resource, then # execute it. def run_action(resource, action, notification_type=nil, notifying_resource=nil) resource.run_action(action, notification_type, notifying_resource) # Execute any immediate and queue up any delayed notifications # associated with the resource, but only if it was updated *this time* # we ran an action on it. if resource.updated_by_last_action? run_context.immediate_notifications(resource).each do |notification| Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)") run_action(notification.resource, notification.action, :immediate, resource) end run_context.delayed_notifications(resource).each do |notification| if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) } Chef::Log.info( "#{resource} not queuing delayed action #{notification.action} on #{notification.resource}"\ " (delayed), as it's already been queued") else delayed_actions << notification end end end end # Iterates over the +resource_collection+ in the +run_context+ calling # +run_action+ for each resource in turn. def converge # Resolve all lazy/forward references in notifications run_context.resource_collection.each do |resource| resource.resolve_notification_references end # Execute each resource. run_context.resource_collection.execute_each_resource do |resource| Array(resource.action).each {|action| run_action(resource, action)} end rescue Exception => e Chef::Log.info "Running queued delayed notifications before re-raising exception" run_delayed_notifications(e) else run_delayed_notifications(nil) true end private # Run all our :delayed actions def run_delayed_notifications(error=nil) collected_failures = Exceptions::MultipleFailures.new collected_failures.client_run_failure(error) unless error.nil? delayed_actions.each do |notification| result = run_delayed_notification(notification) if result.kind_of?(Exception) collected_failures.notification_failure(result) end end collected_failures.raise! end def run_delayed_notification(notification) Chef::Log.info( "#{notification.notifying_resource} sending #{notification.action}"\ " action to #{notification.resource} (delayed)") # Struct of resource/action to call run_action(notification.resource, notification.action, :delayed) true rescue Exception => e e end end end chef-12.3.0/lib/chef/shell/0000755000004100000410000000000012520074675015324 5ustar www-datawww-datachef-12.3.0/lib/chef/shell/shell_session.rb0000644000004100000410000001761712520074675020537 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 'chef/recipe' require 'chef/run_context' require 'chef/config' require 'chef/client' require 'chef/cookbook/cookbook_collection' require 'chef/cookbook_loader' require 'chef/run_list/run_list_expansion' require 'chef/formatters/base' require 'chef/formatters/doc' require 'chef/formatters/minimal' module Shell class ShellSession include Singleton def self.session_type(type=nil) @session_type = type if type @session_type end attr_accessor :node, :compile, :recipe, :run_context attr_reader :node_attributes, :client def initialize @node_built = false formatter = Chef::Formatters.new(Chef::Config.formatter, STDOUT, STDERR) @events = Chef::EventDispatch::Dispatcher.new(formatter) end def node_built? !!@node_built end def reset! loading do rebuild_node @node = client.node shorten_node_inspect Shell::Extensions.extend_context_node(@node) rebuild_context node.consume_attributes(node_attributes) if node_attributes @recipe = Chef::Recipe.new(nil, nil, run_context) Shell::Extensions.extend_context_recipe(@recipe) @node_built = true end end def node_attributes=(attrs) @node_attributes = attrs @node.consume_attributes(@node_attributes) end def resource_collection run_context.resource_collection end def run_context @run_context ||= rebuild_context end def definitions nil end def cookbook_loader nil end def save_node raise "Not Supported! #{self.class.name} doesn't support #save_node, maybe you need to run chef-shell in client mode?" end def rebuild_context raise "Not Implemented! :rebuild_collection should be implemented by subclasses" end private def loading show_loading_progress begin yield rescue => e loading_complete(false) raise e else loading_complete(true) end end def show_loading_progress print "Loading" @loading = true @dot_printer = Thread.new do while @loading print "." sleep 0.5 end end end def loading_complete(success) @loading = false @dot_printer.join msg = success ? "done.\n\n" : "epic fail!\n\n" print msg end def shorten_node_inspect def @node.inspect "" end end def rebuild_node raise "Not Implemented! :rebuild_node should be implemented by subclasses" end end class StandAloneSession < ShellSession session_type :standalone def rebuild_context cookbook_collection = Chef::CookbookCollection.new({}) @run_context = Chef::RunContext.new(@node, cookbook_collection, @events) # no recipes @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", [])) # empty recipe list end private def rebuild_node Chef::Config[:solo] = true @client = Chef::Client.new(nil, Chef::Config[:shell_config]) @client.run_ohai @client.load_node @client.build_node end end class SoloSession < ShellSession session_type :solo def definitions @run_context.definitions end def rebuild_context @run_status = Chef::RunStatus.new(@node, @events) Chef::Cookbook::FileVendor.fetch_from_disk(Chef::Config[:cookbook_path]) cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) @run_context = Chef::RunContext.new(node, cookbook_collection, @events) @run_context.load(Chef::RunList::RunListExpansionFromDisk.new("_default", [])) @run_status.run_context = run_context end private def rebuild_node # Tell the client we're chef solo so it won't try to contact the server Chef::Config[:solo] = true @client = Chef::Client.new(nil, Chef::Config[:shell_config]) @client.run_ohai @client.load_node @client.build_node end end class ClientSession < SoloSession session_type :client def save_node @client.save_node end def rebuild_context @run_status = Chef::RunStatus.new(@node, @events) Chef::Cookbook::FileVendor.fetch_from_remote(Chef::REST.new(Chef::Config[:chef_server_url])) cookbook_hash = @client.sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) @run_context = Chef::RunContext.new(node, cookbook_collection, @events) @run_context.load(@node.run_list.expand(@node.chef_environment)) @run_status.run_context = run_context end private def rebuild_node # Make sure the client knows this is not chef solo Chef::Config[:solo] = false @client = Chef::Client.new(nil, Chef::Config[:shell_config]) @client.run_ohai @client.register @client.load_node @client.build_node end end class DoppelGangerClient < Chef::Client attr_reader :node_name def initialize(node_name) @node_name = node_name @ohai = Ohai::System.new end # Run the very smallest amount of ohai we can get away with and still # hope to have things work. Otherwise we're not very good doppelgangers def run_ohai @ohai.require_plugin('os') end # DoppelGanger implementation of build_node. preserves as many of the node's # attributes, and does not save updates to the server def build_node Chef::Log.debug("Building node object for #{@node_name}") @node = Chef::Node.find_or_create(node_name) ohai_data = @ohai.data.merge(@node.automatic_attrs) @node.consume_external_attrs(ohai_data,nil) @run_list_expansion = @node.expand!('server') @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings Chef::Log.info("Run List is [#{@node.run_list}]") Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]") @node end def register @rest = Chef::REST.new(Chef::Config[:chef_server_url], Chef::Config[:node_name], Chef::Config[:client_key]) end end class DoppelGangerSession < ClientSession session_type "doppelganger client" def save_node puts "A doppelganger should think twice before saving the node" end def assume_identity(node_name) Chef::Config[:doppelganger] = @node_name = node_name reset! rescue Exception => e puts "#{e.class.name}: #{e.message}" puts Array(e.backtrace).join("\n") puts puts "* " * 40 puts "failed to assume the identity of node '#{node_name}', resetting" puts "* " * 40 puts Chef::Config[:doppelganger] = false @node_built = false Shell.session end def rebuild_node # Make sure the client knows this is not chef solo Chef::Config[:solo] = false @client = DoppelGangerClient.new(@node_name) @client.run_ohai @client.register @client.load_node @client.build_node @client.sync_cookbooks end end end chef-12.3.0/lib/chef/shell/ext.rb0000644000004100000410000004203712520074675016457 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009 Daniel DeLeo # 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 'tempfile' require 'chef/recipe' require 'fileutils' require 'chef/dsl/platform_introspection' require 'chef/version' require 'chef/shell/shell_session' require 'chef/shell/model_wrapper' require 'chef/shell/shell_rest' require 'chef/json_compat' module Shell module Extensions Help = Struct.new(:cmd, :desc, :explanation) # Extensions to be included in every 'main' object in chef-shell. # These objects are extended with this module. module ObjectCoreExtensions def ensure_session_select_defined # irb breaks if you prematurely define IRB::JobMangager # so these methods need to be defined at the latest possible time. unless jobs.respond_to?(:select_session_by_context) def jobs.select_session_by_context(&block) @jobs.select { |job| block.call(job[1].context.main)} end end unless jobs.respond_to?(:session_select) def jobs.select_shell_session(target_context) session = if target_context.kind_of?(Class) select_session_by_context { |main| main.kind_of?(target_context) } else select_session_by_context { |main| main.equal?(target_context) } end Array(session.first)[1] end end end def find_or_create_session_for(context_obj) ensure_session_select_defined if subsession = jobs.select_shell_session(context_obj) jobs.switch(subsession) else irb(context_obj) end end def help_banner banner = [] banner << "" banner << "chef-shell Help" banner << "".ljust(80, "=") banner << "| " + "Command".ljust(25) + "| " + "Description" banner << "".ljust(80, "=") self.all_help_descriptions.each do |help_text| banner << "| " + help_text.cmd.ljust(25) + "| " + help_text.desc end banner << "".ljust(80, "=") banner << "\n" banner << "Use help(:command) to get detailed help with individual commands" banner << "\n" banner.join("\n") end def explain_command(method_name) help = self.all_help_descriptions.find { |h| h.cmd.to_s == method_name.to_s } if help puts "" puts "Command: #{method_name}" puts "".ljust(80, "=") puts help.explanation || help.desc puts "".ljust(80, "=") puts "" else puts "" puts "command #{method_name} not found or no help available" puts "" end end # helpfully returns +:on+ so we can have sugary syntax like `tracing on' def on :on end # returns +:off+ so you can just do `tracing off' def off :off end def help_descriptions @help_descriptions ||= [] end def all_help_descriptions help_descriptions end def desc(help_text) @desc = help_text end def explain(explain_text) @explain = explain_text end def subcommands(subcommand_help={}) @subcommand_help = subcommand_help end def singleton_method_added(mname) if @desc help_descriptions << Help.new(mname.to_s, @desc.to_s, @explain) @desc, @explain = nil, nil end if @subcommand_help @subcommand_help.each do |subcommand, text| help_descriptions << Help.new("#{mname}.#{subcommand}", text.to_s, nil) end end @subcommand_help = {} end end module String def on_off_to_bool case self when "on" true when "off" false else self end end end module Symbol def on_off_to_bool self.to_s.on_off_to_bool end end module TrueClass def to_on_off_str "on" end def on_off_to_bool self end end module FalseClass def to_on_off_str "off" end def on_off_to_bool self end end # Methods that have associated help text need to be dynamically added # to the main irb objects, so we define them in a proc and later # instance_eval the proc in the object. ObjectUIExtensions = Proc.new do extend Shell::Extensions::ObjectCoreExtensions desc "prints this help message" explain(<<-E) ## SUMMARY ## When called with no argument, +help+ prints a table of all chef-shell commands. When called with an argument COMMAND, +help+ prints a detailed explanation of the command if available, or the description if no explanation is available. E def help(commmand=nil) if commmand explain_command(commmand) else puts help_banner end :ucanhaz_halp end alias :halp :help desc "prints information about chef" def version puts "This is the chef-shell.\n" + " Chef Version: #{::Chef::VERSION}\n" + " http://www.chef.io/\n" + " http://docs.chef.io/" :ucanhaz_automation end alias :shell :version desc "switch to recipe mode" def recipe_mode find_or_create_session_for Shell.session.recipe :recipe end desc "switch to attributes mode" def attributes_mode find_or_create_session_for Shell.session.node :attributes end desc "run chef using the current recipe" def run_chef Chef::Log.level = :debug session = Shell.session runrun = Chef::Runner.new(session.run_context).converge Chef::Log.level = :info runrun end desc "returns an object to control a paused chef run" subcommands :resume => "resume the chef run", :step => "run only the next resource", :skip_back => "move back in the run list", :skip_forward => "move forward in the run list" def chef_run Shell.session.resource_collection.iterator end desc "resets the current recipe" def reset Shell.session.reset! end desc "assume the identity of another node." def become_node(node_name) Shell::DoppelGangerSession.instance.assume_identity(node_name) :doppelganger end alias :doppelganger :become_node desc "turns printout of return values on or off" def echo(on_or_off) conf.echo = on_or_off.on_off_to_bool end desc "says if echo is on or off" def echo? puts "echo is #{conf.echo.to_on_off_str}" end desc "turns on or off tracing of execution. *verbose*" def tracing(on_or_off) conf.use_tracer = on_or_off.on_off_to_bool tracing? end alias :trace :tracing desc "says if tracing is on or off" def tracing? puts "tracing is #{conf.use_tracer.to_on_off_str}" end alias :trace? :tracing? desc "simple ls style command" def ls(directory) Dir.entries(directory) end end MainContextExtensions = Proc.new do desc "returns the current node (i.e., this host)" def node Shell.session.node end desc "pretty print the node's attributes" def ohai(key=nil) pp(key ? node.attribute[key] : node.attribute) end end RESTApiExtensions = Proc.new do desc "edit an object in your EDITOR" explain(<<-E) ## SUMMARY ## +edit(object)+ allows you to edit any object that can be converted to JSON. When finished editing, this method will return the edited object: new_node = edit(existing_node) ## EDITOR SELECTION ## chef-shell looks for an editor using the following logic 1. Looks for an EDITOR set by Shell.editor = "EDITOR" 2. Looks for an EDITOR configured in your chef-shell config file 3. Uses the value of the EDITOR environment variable E def edit(object) unless Shell.editor puts "Please set your editor with Shell.editor = \"vim|emacs|mate|ed\"" return :failburger end filename = "chef-shell-edit-#{object.class.name}-" if object.respond_to?(:name) filename += object.name elsif object.respond_to?(:id) filename += object.id end edited_data = Tempfile.open([filename, ".js"]) do |tempfile| tempfile.sync = true tempfile.puts Chef::JSONCompat.to_json(object) system("#{Shell.editor.to_s} #{tempfile.path}") tempfile.rewind tempfile.read end Chef::JSONCompat.from_json(edited_data) end desc "Find and edit API clients" explain(<<-E) ## SUMMARY ## +clients+ allows you to query you chef server for information about your api clients. ## LIST ALL CLIENTS ## To see all clients on the system, use clients.all #=> [, ...] If the output from all is too verbose, or you're only interested in a specific value from each of the objects, you can give a code block to +all+: clients.all { |client| client.name } #=> [CLIENT1_NAME, CLIENT2_NAME, ...] ## SHOW ONE CLIENT ## To see a specific client, use clients.show(CLIENT_NAME) ## SEARCH FOR CLIENTS ## You can also search for clients using +find+ or +search+. You can use the familiar string search syntax: clients.search("KEY:VALUE") Just as the +all+ subcommand, the +search+ subcommand can use a code block to filter or transform the information returned from the search: clients.search("KEY:VALUE") { |c| c.name } You can also use a Hash based syntax, multiple search conditions will be joined with AND. clients.find :KEY => :VALUE, :KEY2 => :VALUE2, ... ## BULK-EDIT CLIENTS ## **BE CAREFUL, THIS IS DESTRUCTIVE** You can bulk edit API Clients using the +transform+ subcommand, which requires a code block. Each client will be saved after the code block is run. If the code block returns +nil+ or +false+, that client will be skipped: clients.transform("*:*") do |client| if client.name =~ /borat/i client.admin(false) true else nil end end This will strip the admin privileges from any client named after borat. E subcommands :all => "list all api clients", :show => "load an api client by name", :search => "search for API clients", :transform => "edit all api clients via a code block and save them" def clients @clients ||= Shell::ModelWrapper.new(Chef::ApiClient, :client) end desc "Find and edit cookbooks" subcommands :all => "list all cookbooks", :show => "load a cookbook by name", :transform => "edit all cookbooks via a code block and save them" def cookbooks @cookbooks ||= Shell::ModelWrapper.new(Chef::CookbookVersion) end desc "Find and edit nodes via the API" explain(<<-E) ## SUMMARY ## +nodes+ Allows you to query your chef server for information about your nodes. ## LIST ALL NODES ## You can list all nodes using +all+ or +list+ nodes.all #=> [, , ...] To limit the information returned for each node, pass a code block to the +all+ subcommand: nodes.all { |node| node.name } #=> [NODE1_NAME, NODE2_NAME, ...] ## SHOW ONE NODE ## You can show the data for a single node using the +show+ subcommand: nodes.show("NODE_NAME") => ## SEARCH FOR NODES ## You can search for nodes using the +search+ or +find+ subcommands: nodes.find(:name => "app*") #=> [, ...] Similarly to +all+, you can pass a code block to limit or transform the information returned: nodes.find(:name => "app#") { |node| node.ec2 } ## BULK EDIT NODES ## **BE CAREFUL, THIS OPERATION IS DESTRUCTIVE** Bulk edit nodes by passing a code block to the +transform+ or +bulk_edit+ subcommand. The block will be applied to each matching node, and then the node will be saved. If the block returns +nil+ or +false+, that node will be skipped. nodes.transform do |node| if node.fqdn =~ /.*\\.preprod\\.example\\.com/ node.set[:environment] = "preprod" end end This will assign the attribute to every node with a FQDN matching the regex. E subcommands :all => "list all nodes", :show => "load a node by name", :search => "search for nodes", :transform => "edit all nodes via a code block and save them" def nodes @nodes ||= Shell::ModelWrapper.new(Chef::Node) end desc "Find and edit roles via the API" explain(<<-E) ## SUMMARY ## +roles+ allows you to query and edit roles on your Chef server. ## SUBCOMMANDS ## * all (list) * show (load) * search (find) * transform (bulk_edit) ## SEE ALSO ## See the help for +nodes+ for more information about the subcommands. E subcommands :all => "list all roles", :show => "load a role by name", :search => "search for roles", :transform => "edit all roles via a code block and save them" def roles @roles ||= Shell::ModelWrapper.new(Chef::Role) end desc "Find and edit +databag_name+ via the api" explain(<<-E) ## SUMMARY ## +databags(DATABAG_NAME)+ allows you to query and edit data bag items on your Chef server. Unlike other commands for working with data on the server, +databags+ requires the databag name as an argument, for example: databags(:users).all ## SUBCOMMANDS ## * all (list) * show (load) * search (find) * transform (bulk_edit) ## SEE ALSO ## See the help for +nodes+ for more information about the subcommands. E subcommands :all => "list all items in the data bag", :show => "load a data bag item by id", :search => "search for items in the data bag", :transform => "edit all items via a code block and save them" def databags(databag_name) @named_databags_wrappers ||= {} @named_databags_wrappers[databag_name] ||= Shell::NamedDataBagWrapper.new(databag_name) end desc "Find and edit environments via the API" explain(<<-E) ## SUMMARY ## +environments+ allows you to query and edit environments on your Chef server. ## SUBCOMMANDS ## * all (list) * show (load) * search (find) * transform (bulk_edit) ## SEE ALSO ## See the help for +nodes+ for more information about the subcommands. E subcommands :all => "list all environments", :show => "load an environment by name", :search => "search for environments", :transform => "edit all environments via a code block and save them" def environments @environments ||= Shell::ModelWrapper.new(Chef::Environment) end desc "A REST Client configured to authenticate with the API" def api @rest = Shell::ShellREST.new(Chef::Config[:chef_server_url]) end end RecipeUIExtensions = Proc.new do alias :original_resources :resources desc "list all the resources on the current recipe" def resources(*args) if args.empty? pp run_context.resource_collection.keys else pp resources = original_resources(*args) resources end end end def self.extend_context_object(obj) obj.instance_eval(&ObjectUIExtensions) obj.instance_eval(&MainContextExtensions) obj.instance_eval(&RESTApiExtensions) obj.extend(FileUtils) obj.extend(Chef::DSL::PlatformIntrospection) obj.extend(Chef::DSL::DataQuery) end def self.extend_context_node(node_obj) node_obj.instance_eval(&ObjectUIExtensions) end def self.extend_context_recipe(recipe_obj) recipe_obj.instance_eval(&ObjectUIExtensions) recipe_obj.instance_eval(&RecipeUIExtensions) end end end class String include Shell::Extensions::String end class Symbol include Shell::Extensions::Symbol end class TrueClass include Shell::Extensions::TrueClass end class FalseClass include Shell::Extensions::FalseClass end chef-12.3.0/lib/chef/shell/shell_rest.rb0000644000004100000410000000153012520074675020014 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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. # module Shell class ShellREST < Chef::REST alias :get :get_rest alias :put :put_rest alias :post :post_rest alias :delete :delete_rest end end chef-12.3.0/lib/chef/shell/model_wrapper.rb0000644000004100000410000000554312520074675020520 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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/mixin/convert_to_class_name' require 'chef/mixin/language' module Shell class ModelWrapper include Chef::Mixin::ConvertToClassName attr_reader :model_symbol def initialize(model_class, symbol=nil) @model_class = model_class @model_symbol = symbol || convert_to_snake_case(model_class.name, "Chef").to_sym end def search(query) return all if query.to_s == "all" results = [] Chef::Search::Query.new.search(@model_symbol, format_query(query)) do |obj| if block_given? results << yield(obj) else results << obj end end results end alias :find :search def all(&block) all_objects = list_objects block_given? ? all_objects.map(&block) : all_objects end alias :list :all def show(obj_id) @model_class.load(obj_id) end alias :load :show def transform(what_to_transform, &block) if what_to_transform == :all objects_to_transform = list_objects else objects_to_transform = search(what_to_transform) end objects_to_transform.each do |obj| if result = yield(obj) obj.save end end end alias :bulk_edit :transform private # paper over inconsistencies in the model classes APIs, and return the objects # the user wanted instead of the URI=>object stuff def list_objects objects = @model_class.method(:list).arity == 0? @model_class.list : @model_class.list(true) objects.map { |obj| Array(obj).find {|o| o.kind_of?(@model_class)} } end def format_query(query) if query.respond_to?(:keys) query.map { |key, value| "#{key}:#{value}" }.join(" AND ") else query end end end class NamedDataBagWrapper < ModelWrapper def initialize(databag_name) @model_symbol = @databag_name = databag_name end alias :list :all def show(item) Chef::DataBagItem.load(@databag_name, item) end private def list_objects all_items = [] Chef::Search::Query.new.search(@databag_name) do |item| all_items << item end all_items end end end chef-12.3.0/lib/chef/daemon.rb0000644000004100000410000001031412520074675016004 0ustar www-datawww-data# # Author:: AJ Christensen () # 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. # I love you Merb (lib/merb-core/server.rb) require 'chef/config' require 'chef/run_lock' require 'etc' class Chef class Daemon class << self attr_accessor :name attr_accessor :runlock # Daemonize the current process, managing pidfiles and process uid/gid # # === Parameters # name:: The name to be used for the pid file # def daemonize(name) @name = name @runlock = RunLock.new(pid_file) if runlock.test # We've acquired the daemon lock. Now daemonize. Chef::Log.info("Daemonizing..") begin exit if fork Process.setsid exit if fork Chef::Log.info("Forked, in #{Process.pid}. Privileges: #{Process.euid} #{Process.egid}") File.umask Chef::Config[:umask] $stdin.reopen("/dev/null") $stdout.reopen("/dev/null", "a") $stderr.reopen($stdout) runlock.save_pid rescue NotImplementedError => e Chef::Application.fatal!("There is no fork: #{e.message}") end else Chef::Application.fatal!("Chef is already running pid #{pid_from_file}") end end # Gets the pid file for @name # ==== Returns # String:: # Location of the pid file for @name def pid_file Chef::Config[:pid_file] or "/tmp/#{@name}.pid" end # Suck the pid out of pid_file # ==== Returns # Integer:: # The PID from pid_file # nil:: # Returned if the pid_file does not exist. # def pid_from_file File.read(pid_file).chomp.to_i rescue Errno::ENOENT, Errno::EACCES nil end # Change process user/group to those specified in Chef::Config # def change_privilege Dir.chdir("/") if Chef::Config[:user] and Chef::Config[:group] Chef::Log.info("About to change privilege to #{Chef::Config[:user]}:#{Chef::Config[:group]}") _change_privilege(Chef::Config[:user], Chef::Config[:group]) elsif Chef::Config[:user] Chef::Log.info("About to change privilege to #{Chef::Config[:user]}") _change_privilege(Chef::Config[:user]) end end # Change privileges of the process to be the specified user and group # # ==== Parameters # user:: The user to change the process to. # group:: The group to change the process to. # # ==== Alternatives # If group is left out, the user will be used (changing to user:user) # def _change_privilege(user, group=user) uid, gid = Process.euid, Process.egid begin target_uid = Etc.getpwnam(user).uid rescue ArgumentError => e Chef::Application.fatal!("Failed to get UID for user #{user}, does it exist? #{e.message}") return false end begin target_gid = Etc.getgrnam(group).gid rescue ArgumentError => e Chef::Application.fatal!("Failed to get GID for group #{group}, does it exist? #{e.message}") return false end if (uid != target_uid) or (gid != target_gid) Process.initgroups(user, target_gid) Process::GID.change_privilege(target_gid) Process::UID.change_privilege(target_uid) end true rescue Errno::EPERM => e Chef::Application.fatal!("Permission denied when trying to change #{uid}:#{gid} to #{target_uid}:#{target_gid}. #{e.message}") end end end end chef-12.3.0/lib/chef/environment.rb0000644000004100000410000002174212520074675017114 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Seth Falcon () # Author:: John Keiser () # Author:: Kyle Goodwin () # Copyright:: Copyright 2010-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 'chef/config' require 'chef/mash' require 'chef/mixin/params_validate' require 'chef/mixin/from_file' require 'chef/version_constraint' class Chef class Environment DEFAULT = "default" include Chef::Mixin::ParamsValidate include Chef::Mixin::FromFile attr_accessor :chef_server_rest COMBINED_COOKBOOK_CONSTRAINT = /(.+)(?:[\s]+)((?:#{Chef::VersionConstraint::OPS.join('|')})(?:[\s]+).+)$/.freeze def initialize(chef_server_rest: nil) @name = '' @description = '' @default_attributes = Mash.new @override_attributes = Mash.new @cookbook_versions = Hash.new @chef_server_rest = chef_server_rest end def chef_server_rest @chef_server_rest ||= Chef::REST.new(Chef::Config[:chef_server_url]) end def self.chef_server_rest Chef::REST.new(Chef::Config[:chef_server_url]) end def name(arg=nil) set_or_return( :name, arg, { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String } ) end def description(arg=nil) set_or_return( :description, arg, :kind_of => String ) end def default_attributes(arg=nil) set_or_return( :default_attributes, arg, :kind_of => Hash ) end def default_attributes=(attrs) default_attributes(attrs) end def override_attributes(arg=nil) set_or_return( :override_attributes, arg, :kind_of => Hash ) end def override_attributes=(attrs) override_attributes(attrs) end def cookbook_versions(arg=nil) set_or_return( :cookbook_versions, arg, { :kind_of => Hash, :callbacks => { "should be a valid set of cookbook version requirements" => lambda { |cv| Chef::Environment.validate_cookbook_versions(cv) } } } ) end def cookbook(cookbook, version) validate({ :version => version },{ :version => { :callbacks => { "should be a valid version requirement" => lambda { |v| Chef::Environment.validate_cookbook_version(v) } } } }) @cookbook_versions[cookbook] = version end def to_hash result = { "name" => @name, "description" => @description, "cookbook_versions" => @cookbook_versions, "json_class" => self.class.name, "chef_type" => "environment", "default_attributes" => @default_attributes, "override_attributes" => @override_attributes } result end def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def update_from!(o) description(o.description) cookbook_versions(o.cookbook_versions) default_attributes(o.default_attributes) override_attributes(o.override_attributes) self end def update_attributes_from_params(params) unless params[:default_attributes].nil? || params[:default_attributes].size == 0 default_attributes(Chef::JSONCompat.from_json(params[:default_attributes])) end unless params[:override_attributes].nil? || params[:override_attributes].size == 0 override_attributes(Chef::JSONCompat.from_json(params[:override_attributes])) end end def update_from_params(params) # reset because everything we need will be in the params, this is necessary because certain constraints # may have been removed in the params and need to be removed from cookbook_versions as well. bkup_cb_versions = cookbook_versions cookbook_versions(Hash.new) valid = true begin name(params[:name]) rescue Chef::Exceptions::ValidationFailed => e invalid_fields[:name] = e.message valid = false end description(params[:description]) unless params[:cookbook_version].nil? params[:cookbook_version].each do |index, cookbook_constraint_spec| unless (cookbook_constraint_spec.nil? || cookbook_constraint_spec.size == 0) valid = valid && update_cookbook_constraint_from_param(index, cookbook_constraint_spec) end end end update_attributes_from_params(params) valid = validate_required_attrs_present && valid cookbook_versions(bkup_cb_versions) unless valid # restore the old cookbook_versions if valid is false valid end def update_cookbook_constraint_from_param(index, cookbook_constraint_spec) valid = true md = cookbook_constraint_spec.match(COMBINED_COOKBOOK_CONSTRAINT) if md.nil? || md[2].nil? valid = false add_cookbook_constraint_error(index, cookbook_constraint_spec) elsif self.class.validate_cookbook_version(md[2]) cookbook_versions[md[1]] = md[2] else valid = false add_cookbook_constraint_error(index, cookbook_constraint_spec) end valid end def add_cookbook_constraint_error(index, cookbook_constraint_spec) invalid_fields[:cookbook_version] ||= {} invalid_fields[:cookbook_version][index] = "#{cookbook_constraint_spec} is not a valid cookbook constraint" end def invalid_fields @invalid_fields ||= {} end def validate_required_attrs_present if name.nil? || name.size == 0 invalid_fields[:name] ||= "name cannot be empty" false else true end end def self.json_create(o) environment = new environment.name(o["name"]) environment.description(o["description"]) environment.cookbook_versions(o["cookbook_versions"]) environment.default_attributes(o["default_attributes"]) environment.override_attributes(o["override_attributes"]) environment end def self.list(inflate=false) if inflate response = Hash.new Chef::Search::Query.new.search(:environment) do |e| response[e.name] = e unless e.nil? end response else chef_server_rest.get_rest("environments") end end def self.load(name) if Chef::Config[:solo] load_from_file(name) else chef_server_rest.get_rest("environments/#{name}") end end def self.load_from_file(name) unless File.directory?(Chef::Config[:environment_path]) raise Chef::Exceptions::InvalidEnvironmentPath, "Environment path '#{Chef::Config[:environment_path]}' is invalid" end js_file = File.join(Chef::Config[:environment_path], "#{name}.json") rb_file = File.join(Chef::Config[:environment_path], "#{name}.rb") if File.exists?(js_file) # from_json returns object.class => json_class in the JSON. Chef::JSONCompat.from_json(IO.read(js_file)) elsif File.exists?(rb_file) environment = Chef::Environment.new environment.name(name) environment.from_file(rb_file) environment else raise Chef::Exceptions::EnvironmentNotFound, "Environment '#{name}' could not be loaded from disk" end end def destroy chef_server_rest.delete_rest("environments/#{@name}") end def save begin chef_server_rest.put_rest("environments/#{@name}", self) rescue Net::HTTPServerException => e raise e unless e.response.code == "404" chef_server_rest.post_rest("environments", self) end self end def create chef_server_rest.post_rest("environments", self) self end def self.load_filtered_recipe_list(environment) chef_server_rest.get_rest("environments/#{environment}/recipes") end def to_s @name end def self.validate_cookbook_versions(cv) return false unless cv.kind_of?(Hash) cv.each do |cookbook, version| return false unless Chef::Environment.validate_cookbook_version(version) end true end def self.validate_cookbook_version(version) begin if Chef::Config[:solo] raise Chef::Exceptions::IllegalVersionConstraint, "Environment cookbook version constraints not allowed in chef-solo" else Chef::VersionConstraint.new version true end rescue ArgumentError false end end end end chef-12.3.0/lib/chef/data_bag.rb0000644000004100000410000001134212520074675016265 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # Author:: Christopher Brown () # 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/config' require 'chef/mixin/params_validate' require 'chef/mixin/from_file' require 'chef/data_bag_item' require 'chef/mash' require 'chef/json_compat' class Chef class DataBag include Chef::Mixin::FromFile include Chef::Mixin::ParamsValidate VALID_NAME = /^[\.\-[:alnum:]_]+$/ attr_accessor :chef_server_rest def self.validate_name!(name) unless name =~ VALID_NAME raise Exceptions::InvalidDataBagName, "DataBags must have a name matching #{VALID_NAME.inspect}, you gave #{name.inspect}" end end # Create a new Chef::DataBag def initialize(chef_server_rest: nil) @name = '' @chef_server_rest = chef_server_rest end def name(arg=nil) set_or_return( :name, arg, :regex => VALID_NAME ) end def to_hash result = { 'name' => @name, 'json_class' => self.class.name, 'chef_type' => 'data_bag', } result end # Serialize this object as a hash def to_json(*a) Chef::JSONCompat.to_json(to_hash, *a) end def chef_server_rest @chef_server_rest ||= Chef::REST.new(Chef::Config[:chef_server_url]) end def self.chef_server_rest Chef::REST.new(Chef::Config[:chef_server_url]) end # Create a Chef::Role from JSON def self.json_create(o) bag = new bag.name(o["name"]) bag end def self.list(inflate=false) if Chef::Config[:solo] paths = Array(Chef::Config[:data_bag_path]) names = [] paths.each do |path| unless File.directory?(path) raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid" end names += Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "*")).map{|f|File.basename(f)}.sort end names.inject({}) {|h, n| h[n] = n; h} else if inflate # Can't search for all data bags like other objects, fall back to N+1 :( list(false).inject({}) do |response, bag_and_uri| response[bag_and_uri.first] = load(bag_and_uri.first) response end else Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data") end end end # Load a Data Bag by name via either the RESTful API or local data_bag_path if run in solo mode def self.load(name) if Chef::Config[:solo] paths = Array(Chef::Config[:data_bag_path]) data_bag = {} paths.each do |path| unless File.directory?(path) raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid" end Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path, name.to_s), "*.json")).inject({}) do |bag, f| item = Chef::JSONCompat.from_json(IO.read(f)) # Check if we have multiple items with similar names (ids) and raise if their content differs if data_bag.has_key?(item["id"]) && data_bag[item["id"]] != item raise Chef::Exceptions::DuplicateDataBagItem, "Data bag '#{name}' has items with the same name '#{item["id"]}' but different content." else data_bag[item["id"]] = item end end end return data_bag else Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data/#{name}") end end def destroy chef_server_rest.delete_rest("data/#{@name}") end # Save the Data Bag via RESTful API def save begin if Chef::Config[:why_run] Chef::Log.warn("In whyrun mode, so NOT performing data bag save.") else create end rescue Net::HTTPServerException => e raise e unless e.response.code == "409" end self end #create a data bag via RESTful API def create chef_server_rest.post_rest("data", self) self end # As a string def to_s "data_bag[#{@name}]" end end end chef-12.3.0/lib/chef/cookbook_manifest.rb0000644000004100000410000002325312520074675020243 0ustar www-datawww-data# Author:: Daniel DeLeo () # Copyright:: Copyright 2015 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 'forwardable' require 'chef/util/path_helper' require 'chef/log' class Chef # Handles the details of representing a cookbook in JSON form for uploading # to a Chef Server. class CookbookManifest # Duplicates the same constant in CookbookVersion. We cannot remove it # there because it is treated by other code as part of CookbookVersion's # public API (also used in some deprecated methods). COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ].freeze extend Forwardable attr_reader :cookbook_version def_delegator :@cookbook_version, :root_paths def_delegator :@cookbook_version, :segment_filenames def_delegator :@cookbook_version, :name def_delegator :@cookbook_version, :identifier def_delegator :@cookbook_version, :metadata def_delegator :@cookbook_version, :full_name def_delegator :@cookbook_version, :version def_delegator :@cookbook_version, :frozen_version? # Create a new CookbookManifest object for the given `cookbook_version`. # You can subsequently call #to_hash to get a Hash representation of the # cookbook_version in the "manifest" format, or #to_json to get a JSON # representation of the cookbook_version. # # The inferface for this behavior is expected to change as we implement new # manifest formats. The entire class should be considered a private API for # now. # # @api private # @param policy_mode [Boolean] whether to convert cookbooks to Hash/JSON in # the format used by the `cookbook_artifacts` endpoint (for policyfiles). # Setting this option also changes the behavior of #save_url and # #force_save_url such that CookbookVersions will be uploaded to the new # `cookbook_artifacts` API. This endpoint is currently under active # development and the format is expected to change frequently, therefore # the result of #manifest, #to_hash, and #to_json will not be stable when # `policy_mode` is enabled. def initialize(cookbook_version, policy_mode: false) @cookbook_version = cookbook_version @policy_mode = !!policy_mode reset! end # Resets all lazily computed values. def reset! @manifest = nil @checksums = nil @manifest_records_by_path = nil true end # Returns a 'manifest' data structure that can be uploaded to a Chef # Server. # # The format is as follows: # # { # :cookbook_name => name, # String # :metadata => metadata, # Chef::Cookbook::Metadata # :version => version, # Chef::Version # :name => full_name, # String of "#{name}-#{version}" # # :recipes => Array, # :definitions => Array, # :libraries => Array, # :attributes => Array, # :files => Array, # :templates => Array, # :resources => Array, # :providers => Array, # :root_files => Array # } # # Where a `FileSpec` is a Hash of the form: # # { # :name => file_name, # :path => path, # :checksum => csum, # :specificity => specificity # } # def manifest @manifest || generate_manifest @manifest end def checksums @manifest || generate_manifest @checksums end def manifest_records_by_path @manifest || generate_manifest @manifest_records_by_path end def policy_mode? @policy_mode end def to_hash result = manifest.dup result['frozen?'] = frozen_version? result['chef_type'] = 'cookbook_version' result.to_hash end def to_json(*a) result = to_hash result['json_class'] = "Chef::CookbookVersion" Chef::JSONCompat.to_json(result, *a) end # Return the URL to save (PUT) this object to the server via the # REST api. If there is an existing document on the server and it # is marked frozen, a PUT will result in a 409 Conflict. def save_url if policy_mode? "#{named_cookbook_url}/#{identifier}" else "#{named_cookbook_url}/#{version}" end end def named_cookbook_url "#{cookbook_url_path}/#{name}" end # Adds the `force=true` parameter to the upload URL. This allows # the user to overwrite a frozen cookbook (a PUT against the # normal #save_url raises a 409 Conflict in this case). def force_save_url "#{save_url}?force=true" end # Update this CookbookManifest from the contents of another manifest, and # make the corresponding changes to the cookbook_version object. Required # to provide backward compatibility with CookbookVersion#manifest= method. def update_from(new_manifest) @manifest = Mash.new new_manifest @checksums = extract_checksums_from_manifest(@manifest) @manifest_records_by_path = extract_manifest_records_by_path(@manifest) COOKBOOK_SEGMENTS.each do |segment| next unless @manifest.has_key?(segment) filenames = @manifest[segment].map{|manifest_record| manifest_record['name']} cookbook_version.replace_segment_filenames(segment, filenames) end end private def cookbook_url_path policy_mode? ? "cookbook_artifacts" : "cookbooks" end # See #manifest for a description of the manifest return value. # See #preferred_manifest_record for a description an individual manifest record. def generate_manifest manifest = Mash.new({ :recipes => Array.new, :definitions => Array.new, :libraries => Array.new, :attributes => Array.new, :files => Array.new, :templates => Array.new, :resources => Array.new, :providers => Array.new, :root_files => Array.new }) @checksums = {} if !root_paths || root_paths.size == 0 Chef::Log.error("Cookbook #{name} does not have root_paths! Cannot generate manifest.") raise "Cookbook #{name} does not have root_paths! Cannot generate manifest." end COOKBOOK_SEGMENTS.each do |segment| segment_filenames(segment).each do |segment_file| next if File.directory?(segment_file) path, specificity = parse_segment_file_from_root_paths(segment, segment_file) file_name = File.basename(path) csum = checksum_cookbook_file(segment_file) @checksums[csum] = segment_file rs = Mash.new({ :name => file_name, :path => path, :checksum => csum, :specificity => specificity }) manifest[segment] << rs end end manifest[:metadata] = metadata manifest[:version] = metadata.version if policy_mode? manifest[:name] = name.to_s manifest[:identifier] = identifier else manifest[:name] = full_name manifest[:cookbook_name] = name.to_s end @manifest_records_by_path = extract_manifest_records_by_path(manifest) @manifest = manifest end def parse_segment_file_from_root_paths(segment, segment_file) root_paths.each do |root_path| pathname = Chef::Util::PathHelper.relative_path_from(root_path, segment_file) parts = pathname.each_filename.take(2) # Check if path is actually under root_path next if parts[0] == '..' if segment == :templates || segment == :files # Check if pathname looks like files/foo or templates/foo (unscoped) if pathname.each_filename.to_a.length == 2 # Use root_default in case the same path exists at root_default and default return [ pathname.to_s, 'root_default' ] else return [ pathname.to_s, parts[1] ] end else return [ pathname.to_s, 'default' ] end end Chef::Log.error("Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}.") raise "Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}." end def extract_checksums_from_manifest(manifest) checksums = {} COOKBOOK_SEGMENTS.each do |segment| next unless manifest.has_key?(segment) manifest[segment].each do |manifest_record| checksums[manifest_record[:checksum]] = nil end end checksums end def checksum_cookbook_file(filepath) CookbookVersion.checksum_cookbook_file(filepath) end def extract_manifest_records_by_path(manifest) manifest_records_by_path = {} COOKBOOK_SEGMENTS.each do |segment| next unless manifest.has_key?(segment) manifest[segment].each do |manifest_record| manifest_records_by_path[manifest_record[:path]] = manifest_record end end manifest_records_by_path end end end chef-12.3.0/lib/chef/resource_definition.rb0000644000004100000410000000371012520074675020602 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/mixin/from_file' require 'chef/mixin/params_validate' class Chef class ResourceDefinition include Chef::Mixin::FromFile include Chef::Mixin::ParamsValidate attr_accessor :name, :params, :recipe, :node def initialize(node=nil) @name = nil @params = Hash.new @recipe = nil @node = node end def define(resource_name, prototype_params=nil, &block) unless resource_name.kind_of?(Symbol) raise ArgumentError, "You must use a symbol when defining a new resource!" end @name = resource_name if prototype_params unless prototype_params.kind_of?(Hash) raise ArgumentError, "You must pass a hash as the prototype parameters for a definition." end @params = prototype_params end if Kernel.block_given? @recipe = block else raise ArgumentError, "You must pass a block to a definition." end true end # When we do the resource definition, we're really just setting new values for # the parameters we prototyped at the top. This method missing is as simple as # it gets. def method_missing(symbol, *args) @params[symbol] = args.length == 1 ? args[0] : args end def to_s "#{name.to_s}" end end end chef-12.3.0/lib/chef/http/0000755000004100000410000000000012520074675015174 5ustar www-datawww-datachef-12.3.0/lib/chef/http/http_request.rb0000644000004100000410000001271612520074675020257 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010 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 'uri' require 'net/http' # To load faster, we only want ohai's version string. # However, in ohai before 0.6.0, the version is defined # in ohai, not ohai/version begin require 'ohai/version' #used in user agent string. rescue LoadError require 'ohai' end require 'chef/version' class Chef class HTTP class HTTPRequest engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby" UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +http://opscode.com)" DEFAULT_UA = "Chef Client" << UA_COMMON USER_AGENT = "User-Agent".freeze ACCEPT_ENCODING = "Accept-Encoding".freeze ENCODING_GZIP_DEFLATE = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3".freeze GET = "get".freeze PUT = "put".freeze POST = "post".freeze DELETE = "delete".freeze HEAD = "head".freeze HTTPS = "https".freeze SLASH = "/".freeze HOST_LOWER = "host".freeze URI_SCHEME_DEFAULT_PORT = { 'http' => 80, 'https' => 443 }.freeze def self.user_agent=(ua) @user_agent = ua end def self.user_agent @user_agent ||= DEFAULT_UA end attr_reader :method, :url, :headers, :http_client, :http_request def initialize(method, url, req_body, base_headers={}) @method, @url = method, url @request_body = nil build_headers(base_headers) configure_http_request(req_body) end def host @url.hostname end def uri_safe_host @url.host end def port @url.port end def query @url.query end def path @url.path.empty? ? SLASH : @url.path end # DEPRECATED. Call request on an HTTP client object instead. def call hide_net_http_bug do http_client.request(http_request) do |response| yield response if block_given? response end end end def config Chef::Config end # DEPRECATED. Call request on an HTTP client object instead. def http_client @http_client ||= BasicClient.new(url).http_client end private def hide_net_http_bug yield rescue NoMethodError => e # http://redmine.ruby-lang.org/issues/show/2708 # http://redmine.ruby-lang.org/issues/show/2758 if e.to_s =~ /#{Regexp.escape(%q|undefined method `closed?' for nil:NilClass|)}/ Chef::Log.debug("Rescued error in http connect, re-raising as Errno::ECONNREFUSED to hide bug in net/http") Chef::Log.debug("#{e.class.name}: #{e.to_s}") Chef::Log.debug(e.backtrace.join("\n")) raise Errno::ECONNREFUSED, "Connection refused attempting to contact #{url.scheme}://#{host}:#{port}" else raise end end def build_headers(headers) @headers = headers.dup # No response compression unless we asked for it explicitly: @headers[HTTPRequest::ACCEPT_ENCODING] ||= "identity" @headers['X-Chef-Version'] = ::Chef::VERSION # Only include port in Host header when it is not the default port # for the url scheme (80;443) - Fixes CHEF-5355 host_header = uri_safe_host.dup host_header << ":#{port}" unless URI_SCHEME_DEFAULT_PORT[@url.scheme] == port.to_i @headers['Host'] = host_header unless @headers.keys.any? {|k| k.downcase.to_s == HOST_LOWER } @headers end def configure_http_request(request_body=nil) req_path = "#{path}" req_path << "?#{query}" if query @http_request = case method.to_s.downcase when GET Net::HTTP::Get.new(req_path, headers) when POST Net::HTTP::Post.new(req_path, headers) when PUT Net::HTTP::Put.new(req_path, headers) when DELETE Net::HTTP::Delete.new(req_path, headers) when HEAD Net::HTTP::Head.new(req_path, headers) else raise ArgumentError, "You must provide :GET, :PUT, :POST, :DELETE or :HEAD as the method" end @http_request.body = request_body if (request_body && @http_request.request_body_permitted?) # Optionally handle HTTP Basic Authentication if url.user user = URI.unescape(url.user) password = URI.unescape(url.password) if url.password @http_request.basic_auth(user, password) end # Overwrite default UA @http_request[USER_AGENT] = self.class.user_agent end end end end chef-12.3.0/lib/chef/http/auth_credentials.rb0000644000004100000410000000415712520074675021046 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010 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/log' require 'mixlib/authentication/signedheaderauth' class Chef class HTTP class AuthCredentials attr_reader :client_name, :key def initialize(client_name=nil, key=nil) @client_name, @key = client_name, key end def sign_requests? !!key end def signature_headers(request_params={}) raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if client_name.nil? Chef::Log.debug("Signing the request as #{client_name}") # params_in = {:http_method => :GET, :path => "/clients", :body => "", :host => "localhost"} request_params = request_params.dup request_params[:timestamp] = Time.now.utc.iso8601 request_params[:user_id] = client_name request_params[:proto_version] = Chef::Config[:authentication_protocol_version] host = request_params.delete(:host) || "localhost" sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params) signed = sign_obj.sign(key).merge({:host => host}) signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo} end end end end chef-12.3.0/lib/chef/http/simple.rb0000644000004100000410000000071712520074675017017 0ustar www-datawww-datarequire 'chef/http' require 'chef/http/authenticator' require 'chef/http/decompressor' require 'chef/http/cookie_manager' require 'chef/http/validate_content_length' class Chef class HTTP class Simple < HTTP use Decompressor use CookieManager # ValidateContentLength should come after Decompressor # because the order of middlewares is reversed when handling # responses. use ValidateContentLength end end end chef-12.3.0/lib/chef/http/cookie_manager.rb0000644000004100000410000000352612520074675020472 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/http/cookie_jar' class Chef class HTTP # An HTTP middleware to manage storing/sending cookies in HTTP requests. # Most HTTP communication in Chef does not need cookies, it was originally # implemented to support OpenID, but it's not known who might be relying on # it, so it's included with Chef::REST class CookieManager def initialize(options={}) @cookies = CookieJar.instance end def handle_request(method, url, headers={}, data=false) @host, @port = url.host, url.port if @cookies.has_key?("#{@host}:#{@port}") headers['Cookie'] = @cookies["#{@host}:#{@port}"] end [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) if http_response['set-cookie'] @cookies["#{@host}:#{@port}"] = http_response['set-cookie'] end [http_response, rest_request, return_value] end def stream_response_handler(response) nil end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end end end end chef-12.3.0/lib/chef/http/json_output.rb0000644000004100000410000000524412520074675020117 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Author:: John Keiser () # 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/json_compat' require 'chef/log' class Chef class HTTP # Middleware that takes an HTTP response, parses it as JSON if possible. class JSONOutput attr_accessor :raw_output attr_accessor :inflate_json_class def initialize(opts={}) @raw_output = opts[:raw_output] @inflate_json_class = opts[:inflate_json_class] end def handle_request(method, url, headers={}, data=false) # Ideally this should always set Accept to application/json, but # Chef::REST is sometimes used to make non-JSON requests, so it sets # Accept to the desired value before middlewares get called. headers['Accept'] ||= 'application/json' [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) # temporary hack, skip processing if return_value is false # needed to keep conditional get stuff working correctly. return [http_response, rest_request, return_value] if return_value == false if http_response['content-type'] =~ /json/ if http_response.body.nil? return_value = nil elsif raw_output return_value = http_response.body.to_s else if inflate_json_class return_value = Chef::JSONCompat.from_json(http_response.body.chomp) else return_value = Chef::JSONCompat.parse(http_response.body.chomp) end end [http_response, rest_request, return_value] else Chef::Log.debug("Expected JSON response, but got content-type '#{http_response['content-type']}'") return [http_response, rest_request, http_response.body.to_s] end end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def stream_response_handler(response) nil end end end end chef-12.3.0/lib/chef/http/validate_content_length.rb0000644000004100000410000000725612520074675022417 0ustar www-datawww-data#-- # Author:: Lamont Granquist () # Copyright:: Copyright (c) 2013 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'pp' require 'chef/log' class Chef class HTTP # Middleware that validates the Content-Length header against the downloaded number of bytes. # # This must run before the decompressor middleware, since otherwise we will count the uncompressed # streamed bytes, rather than the on-the-wire compressed bytes. class ValidateContentLength class ContentLengthCounter attr_accessor :content_length def initialize @content_length = 0 end def handle_chunk(chunk) @content_length += chunk.bytesize chunk end end def initialize(opts={}) end def handle_request(method, url, headers={}, data=false) [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) validate(http_response, http_response.body.bytesize) if http_response && http_response.body return [http_response, rest_request, return_value] end def handle_stream_complete(http_response, rest_request, return_value) if @content_length_counter.nil? Chef::Log.debug("No content-length information collected for the streamed download, cannot identify streamed download.") else validate(http_response, @content_length_counter.content_length) end # Make sure the counter is reset since this object might get used # again. See CHEF-5100 @content_length_counter = nil return [http_response, rest_request, return_value] end def stream_response_handler(response) @content_length_counter = ContentLengthCounter.new end private def response_content_length(response) return nil if response['content-length'].nil? if response['content-length'].is_a?(Array) response['content-length'].first.to_i else response['content-length'].to_i end end def validate(http_response, response_length) content_length = response_content_length(http_response) transfer_encoding = http_response['transfer-encoding'] content_encoding = http_response['content-encoding'] if content_length.nil? Chef::Log.debug "HTTP server did not include a Content-Length header in response, cannot identify truncated downloads." return true end # if Transfer-Encoding is set the RFC states that we must ignore the Content-Length field # CHEF-5041: some proxies uncompress gzip content, leave the incorrect content-length, but set the transfer-encoding field unless transfer_encoding.nil? Chef::Log.debug "Transfer-Encoding header is set, skipping Content-Length check." return true end if response_length != content_length raise Chef::Exceptions::ContentLengthMismatch.new(response_length, content_length) end Chef::Log.debug "Content-Length validated correctly." true end end end end chef-12.3.0/lib/chef/http/basic_client.rb0000644000004100000410000001276512520074675020153 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010 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 'uri' require 'net/http' require 'chef/http/ssl_policies' require 'chef/http/http_request' class Chef class HTTP class BasicClient HTTPS = "https".freeze attr_reader :url attr_reader :http_client attr_reader :ssl_policy # Instantiate a BasicClient. # === Arguments: # url:: An URI for the remote server. # === Options: # ssl_policy:: The SSL Policy to use, defaults to DefaultSSLPolicy def initialize(url, opts={}) @url = url @ssl_policy = opts[:ssl_policy] || DefaultSSLPolicy @http_client = build_http_client end def host @url.hostname end def port @url.port end def request(method, url, req_body, base_headers={}) http_request = HTTPRequest.new(method, url, req_body, base_headers).http_request Chef::Log.debug("Initiating #{method} to #{url}") Chef::Log.debug("---- HTTP Request Header Data: ----") base_headers.each do |name, value| Chef::Log.debug("#{name}: #{value}") end Chef::Log.debug("---- End HTTP Request Header Data ----") http_client.request(http_request) do |response| Chef::Log.debug("---- HTTP Status and Header Data: ----") Chef::Log.debug("HTTP #{response.http_version} #{response.code} #{response.msg}") response.each do |header, value| Chef::Log.debug("#{header}: #{value}") end Chef::Log.debug("---- End HTTP Status/Header Data ----") # For non-400's, log the request and response bodies if !response.code || !response.code.start_with?('2') if response.body Chef::Log.debug("---- HTTP Response Body ----") Chef::Log.debug(response.body) Chef::Log.debug("---- End HTTP Response Body -----") end if req_body Chef::Log.debug("---- HTTP Request Body ----") Chef::Log.debug(req_body) Chef::Log.debug("---- End HTTP Request Body ----") end end yield response if block_given? # http_client.request may not have the return signature we want, so # force the issue: return [http_request, response] end rescue OpenSSL::SSL::SSLError => e Chef::Log.error("SSL Validation failure connecting to host: #{host} - #{e.message}") raise end #adapted from buildr/lib/buildr/core/transports.rb def proxy_uri proxy = Chef::Config["#{url.scheme}_proxy"] || env["#{url.scheme.upcase}_PROXY"] || env["#{url.scheme}_proxy"] # Check if the proxy string contains a scheme. If not, add the url's scheme to the # proxy before parsing. The regex /^.*:\/\// matches, for example, http://. proxy = if proxy.match(/^.*:\/\//) URI.parse(proxy) else URI.parse("#{url.scheme}://#{proxy}") end if String === proxy no_proxy = Chef::Config[:no_proxy] || env['NO_PROXY'] || env['no_proxy'] excludes = no_proxy.to_s.split(/\s*,\s*/).compact excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" } return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") } end def build_http_client http_client = http_client_builder.new(host, port) if url.scheme == HTTPS configure_ssl(http_client) end http_client.read_timeout = config[:rest_timeout] http_client.open_timeout = config[:rest_timeout] http_client end def config Chef::Config end def env ENV end def http_client_builder http_proxy = proxy_uri if http_proxy.nil? Net::HTTP else Chef::Log.debug("Using #{http_proxy.host}:#{http_proxy.port} for proxy") user = http_proxy_user(http_proxy) pass = http_proxy_pass(http_proxy) Net::HTTP.Proxy(http_proxy.host, http_proxy.port, user, pass) end end def http_proxy_user(http_proxy) http_proxy.user || Chef::Config["#{url.scheme}_proxy_user"] || env["#{url.scheme.upcase}_PROXY_USER"] || env["#{url.scheme}_proxy_user"] end def http_proxy_pass(http_proxy) http_proxy.password || Chef::Config["#{url.scheme}_proxy_pass"] || env["#{url.scheme.upcase}_PROXY_PASS"] || env["#{url.scheme}_proxy_pass"] end def configure_ssl(http_client) http_client.use_ssl = true ssl_policy.apply_to(http_client) end end end end chef-12.3.0/lib/chef/http/json_to_model_output.rb0000644000004100000410000000214012520074675021771 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/http/json_output' class Chef class HTTP # A Middleware-ish thing that takes an HTTP response, parses it as JSON if # possible, and converts it into an appropriate model object if it contains # a `json_class` key. class JSONToModelOutput < JSONOutput def initialize(opts={}) opts[:inflate_json_class] = true if !opts.has_key?(:inflate_json_class) super end end end end chef-12.3.0/lib/chef/http/ssl_policies.rb0000644000004100000410000001052712520074675020216 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010, 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 'openssl' require 'chef/util/path_helper' class Chef class HTTP # == Chef::HTTP::DefaultSSLPolicy # Configures SSL behavior on an HTTP object via visitor pattern. class DefaultSSLPolicy def self.apply_to(http_client) new(http_client).apply http_client end attr_reader :http_client def initialize(http_client) @http_client = http_client end def apply set_verify_mode set_ca_store set_custom_certs set_client_credentials end def set_verify_mode if config[:ssl_verify_mode] == :verify_none http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE elsif config[:ssl_verify_mode] == :verify_peer http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER end end def set_ca_store if config[:ssl_ca_path] unless ::File.exist?(config[:ssl_ca_path]) raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_path #{config[:ssl_ca_path]} does not exist" end http_client.ca_path = config[:ssl_ca_path] elsif config[:ssl_ca_file] unless ::File.exist?(config[:ssl_ca_file]) raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{config[:ssl_ca_file]} does not exist" end http_client.ca_file = config[:ssl_ca_file] end end def set_custom_certs unless http_client.cert_store http_client.cert_store = OpenSSL::X509::Store.new http_client.cert_store.set_default_paths end if config.trusted_certs_dir certs = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(config.trusted_certs_dir), "*.{crt,pem}")) certs.each do |cert_file| cert = OpenSSL::X509::Certificate.new(File.read(cert_file)) add_trusted_cert(cert) end end end def set_client_credentials if (config[:ssl_client_cert] || config[:ssl_client_key]) unless (config[:ssl_client_cert] && config[:ssl_client_key]) raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together" end unless ::File.exists?(config[:ssl_client_cert]) raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist" end unless ::File.exists?(config[:ssl_client_key]) raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist" end http_client.cert = OpenSSL::X509::Certificate.new(::File.read(config[:ssl_client_cert])) http_client.key = OpenSSL::PKey::RSA.new(::File.read(config[:ssl_client_key])) end end def config Chef::Config end private def add_trusted_cert(cert) http_client.cert_store.add_cert(cert) rescue OpenSSL::X509::StoreError => e raise e unless e.message == 'cert already in hash table' end end class APISSLPolicy < DefaultSSLPolicy def set_verify_mode if config[:ssl_verify_mode] == :verify_peer or config[:verify_api_cert] http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER elsif config[:ssl_verify_mode] == :verify_none http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE end end end end end chef-12.3.0/lib/chef/http/cookie_jar.rb0000644000004100000410000000201112520074675017620 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Thom May () # Author:: Nuo Yan () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2009, 2010 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 'singleton' class Chef class HTTP class CookieJar < Hash include Singleton end end end chef-12.3.0/lib/chef/http/authenticator.rb0000644000004100000410000000612712520074675020401 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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/http/auth_credentials' require 'chef/exceptions' require 'openssl' class Chef class HTTP class Authenticator attr_reader :signing_key_filename attr_reader :raw_key attr_reader :attr_names attr_reader :auth_credentials attr_accessor :sign_request def initialize(opts={}) @raw_key = nil @sign_request = true @signing_key_filename = opts[:signing_key_filename] @key = load_signing_key(opts[:signing_key_filename], opts[:raw_key]) @auth_credentials = AuthCredentials.new(opts[:client_name], @key) end def handle_request(method, url, headers={}, data=false) headers.merge!(authentication_headers(method, url, data)) if sign_requests? [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def stream_response_handler(response) nil end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def sign_requests? auth_credentials.sign_requests? && @sign_request end def client_name @auth_credentials.client_name end def load_signing_key(key_file, raw_key = nil) if (!!key_file) @raw_key = IO.read(key_file).strip elsif (!!raw_key) @raw_key = raw_key.strip else return nil end @key = OpenSSL::PKey::RSA.new(@raw_key) rescue SystemCallError, IOError => e Chef::Log.warn "Failed to read the private key #{key_file}: #{e.inspect}" raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!" rescue OpenSSL::PKey::RSAError msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key.\n" msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'" raise Chef::Exceptions::InvalidPrivateKey, msg end def authentication_headers(method, url, json_body=nil) request_params = {:http_method => method, :path => url.path, :body => json_body, :host => "#{url.host}:#{url.port}"} request_params[:body] ||= "" auth_credentials.signature_headers(request_params) end end end end chef-12.3.0/lib/chef/http/decompressor.rb0000644000004100000410000001117012520074675020226 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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 'zlib' require 'chef/http/http_request' class Chef class HTTP # Middleware-esque class for handling compression in HTTP responses. class Decompressor class NoopInflater def inflate(chunk) chunk end alias :handle_chunk :inflate end class GzipInflater < Zlib::Inflate def initialize super(Zlib::MAX_WBITS + 16) end alias :handle_chunk :inflate end class DeflateInflater < Zlib::Inflate def initialize super end alias :handle_chunk :inflate end CONTENT_ENCODING = "content-encoding".freeze GZIP = "gzip".freeze DEFLATE = "deflate".freeze IDENTITY = "identity".freeze def initialize(opts={}) @disable_gzip = false handle_options(opts) end def handle_request(method, url, headers={}, data=false) headers[HTTPRequest::ACCEPT_ENCODING] = HTTPRequest::ENCODING_GZIP_DEFLATE unless gzip_disabled? [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) # temporary hack, skip processing if return_value is false # needed to keep conditional get stuff working correctly. return [http_response, rest_request, return_value] if return_value == false response_body = decompress_body(http_response) http_response.body.replace(response_body) if http_response.body.respond_to?(:replace) [http_response, rest_request, return_value] end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def decompress_body(response) if gzip_disabled? || response.body.nil? response.body else case response[CONTENT_ENCODING] when GZIP Chef::Log.debug "decompressing gzip response" Zlib::Inflate.new(Zlib::MAX_WBITS + 16).inflate(response.body) when DEFLATE Chef::Log.debug "decompressing deflate response" Zlib::Inflate.inflate(response.body) else response.body end end end # This isn't used when this class is used as middleware; it returns an # object you can use to unzip/inflate a streaming response. def stream_response_handler(response) if gzip_disabled? Chef::Log.debug "disable_gzip is set. \ Not using #{response[CONTENT_ENCODING]} \ and initializing noop stream deflator." NoopInflater.new else case response[CONTENT_ENCODING] when GZIP Chef::Log.debug "Initializing gzip stream deflator" GzipInflater.new when DEFLATE Chef::Log.debug "Initializing deflate stream deflator" DeflateInflater.new else Chef::Log.debug "content_encoding = '#{response[CONTENT_ENCODING]}' \ initializing noop stream deflator." NoopInflater.new end end end # gzip is disabled using the disable_gzip => true option in the # constructor. When gzip is disabled, no 'Accept-Encoding' header will be # set, and the response will not be decompressed, no matter what the # Content-Encoding header of the response is. The intended use case for # this is to work around situations where you request +file.tar.gz+, but # the server responds with a content type of tar and a content encoding of # gzip, tricking the client into decompressing the response so you end up # with a tar archive (no gzip) named file.tar.gz def gzip_disabled? @disable_gzip end private def handle_options(opts) opts.each do |name, value| case name.to_s when 'disable_gzip' @disable_gzip = value end end end end end end chef-12.3.0/lib/chef/http/remote_request_id.rb0000644000004100000410000000254312520074675021244 0ustar www-datawww-data# Author:: Prajakta Purohit () # Copyright:: Copyright (c) 2009, 2010, 2013, 2014 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/request_id' class Chef class HTTP class RemoteRequestID def initialize(opts={}) end def handle_request(method, url, headers={}, data=false) headers.merge!({'X-REMOTE-REQUEST-ID' => Chef::RequestID.instance.request_id}) [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def stream_response_handler(response) nil end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end end end end chef-12.3.0/lib/chef/http/json_input.rb0000644000004100000410000000441512520074675017715 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Author:: John Keiser () # 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/json_compat' class Chef class HTTP # Middleware that takes json input and turns it into raw text class JSONInput def initialize(opts={}) end def handle_request(method, url, headers={}, data=false) if data && should_encode_as_json?(headers) headers.delete_if { |key, _value| key.downcase == 'content-type' } headers["Content-Type"] = 'application/json' data = Chef::JSONCompat.to_json(data) # Force encoding to binary to fix SSL related EOFErrors # cf. http://tickets.opscode.com/browse/CHEF-2363 # http://redmine.ruby-lang.org/issues/5233 data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding) end [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) [http_response, rest_request, return_value] end def stream_response_handler(response) nil end def handle_stream_complete(http_response, rest_request, return_value) [http_response, rest_request, return_value] end private def should_encode_as_json?(headers) # ruby/Net::HTTP don't enforce capitalized headers (it normalizes them # for you before sending the request), so we have to account for all # the variations we might find requested_content_type = headers.find {|k, v| k.downcase == "content-type" } requested_content_type.nil? || requested_content_type.last.include?("json") end end end end chef-12.3.0/lib/chef/http/socketless_chef_zero_client.rb0000644000004100000410000001547012520074675023271 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # --- # Some portions of the code in this file are verbatim copies of code from the # fakeweb project: https://github.com/chrisk/fakeweb # # fakeweb is distributed under the MIT license, which is copied below: # --- # # Copyright 2006-2010 Blaine Cook, Chris Kampmeier, and other contributors # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. require 'chef_zero/server' class Chef class HTTP # HTTP Client class that talks directly to Zero via the Rack interface. class SocketlessChefZeroClient # This module is extended into Net::HTTP Response objects created from # Socketless Chef Zero responses. module ResponseExts # Net::HTTP raises an error if #read_body is called with a block or # file argument after the body has already been read from the network. # # Since we always set the body to the string response from Chef Zero # and set the `@read` indicator variable, we have to patch this method # or else streaming-style responses won't work. def read_body(dest = nil, &block) if dest raise "responses from socketless chef zero can't be written to specific destination" end if block_given? block.call(@body) else super end end end attr_reader :url # copied verbatim from webrick (2-clause BSD License) # # HTTP status codes and descriptions STATUS_MESSAGE = { 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Request Range Not Satisfiable', 417 => 'Expectation Failed', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 507 => 'Insufficient Storage', 511 => 'Network Authentication Required', } STATUS_MESSAGE.values.each {|v| v.freeze } STATUS_MESSAGE.freeze def initialize(base_url) @url = base_url end def host @url.hostname end def port @url.port end def request(method, url, body, headers, &handler_block) request = req_to_rack(method, url, body, headers) res = ChefZero::SocketlessServerMap.request(port, request) net_http_response = to_net_http(res[0], res[1], res[2]) yield net_http_response if block_given? [self, net_http_response] end def req_to_rack(method, url, body, headers) body_str = body || "" { "SCRIPT_NAME" => "", "SERVER_NAME" => "localhost", "REQUEST_METHOD" => method.to_s.upcase, "PATH_INFO" => url.path, "QUERY_STRING" => url.query, "SERVER_PORT" => url.port, "HTTP_HOST" => "localhost:#{url.port}", "rack.url_scheme" => "chefzero", "rack.input" => StringIO.new(body_str), } end def to_net_http(code, headers, chunked_body) body = chunked_body.join('') msg = STATUS_MESSAGE[code] or raise "Cannot determine HTTP status message for code #{code}" response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg) response.instance_variable_set(:@body, body) headers.each do |name, value| if value.respond_to?(:each) value.each { |v| response.add_field(name, v) } else response[name] = value end end response.instance_variable_set(:@read, true) response.extend(ResponseExts) response end private def headers_extracted_from_options options.reject {|name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value| [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value] } end end end end chef-12.3.0/lib/chef/handler/0000755000004100000410000000000012520074675015632 5ustar www-datawww-datachef-12.3.0/lib/chef/handler/error_report.rb0000644000004100000410000000204512520074675020704 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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/handler' require 'chef/resource/directory' class Chef class Handler class ErrorReport < ::Chef::Handler def report Chef::FileCache.store("failed-run-data.json", Chef::JSONCompat.to_json_pretty(data), 0640) Chef::Log.fatal("Saving node information to #{Chef::FileCache.load("failed-run-data.json", false)}") end end end end chef-12.3.0/lib/chef/handler/json_file.rb0000644000004100000410000000351512520074675020133 0ustar www-datawww-data# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 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/handler' require 'chef/resource/directory' class Chef class Handler class JsonFile < ::Chef::Handler attr_reader :config def initialize(config={}) @config = config @config[:path] ||= "/var/chef/reports" @config end def report if exception Chef::Log.error("Creating JSON exception report") else Chef::Log.info("Creating JSON run report") end build_report_dir savetime = Time.now.strftime("%Y%m%d%H%M%S") File.open(File.join(config[:path], "chef-run-report-#{savetime}.json"), "w") do |file| #ensure start time and end time are output in the json properly in the event activesupport happens to be on the system run_data = data run_data[:start_time] = run_data[:start_time].to_s run_data[:end_time] = run_data[:end_time].to_s file.puts Chef::JSONCompat.to_json_pretty(run_data) end end def build_report_dir unless File.exists?(config[:path]) FileUtils.mkdir_p(config[:path]) File.chmod(00700, config[:path]) end end end end end chef-12.3.0/lib/chef/util/0000755000004100000410000000000012520074675015172 5ustar www-datawww-datachef-12.3.0/lib/chef/util/diff.rb0000644000004100000410000001544012520074675016433 0ustar www-datawww-data# Author:: Lamont Granquist () # 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. # # Some portions of this file are derived from material in the diff-lcs # project licensed under the terms of the MIT license, provided below. # # Copyright:: Copyright (c) 2004-2013 Austin Ziegler # License:: MIT # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of this Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE # SOFTWARE. require 'diff/lcs' require 'diff/lcs/hunk' class Chef class Util class Diff # @todo: to_a, to_s, to_json, inspect defs, accessors for @diff and @error # @todo: move coercion to UTF-8 into to_json # @todo: replace shellout to diff -u with diff-lcs gem def for_output # formatted output to a terminal uses arrays of strings and returns error strings @diff.nil? ? [ @error ] : @diff end def for_reporting # caller needs to ensure that new files aren't posted to resource reporting return nil if @diff.nil? @diff.join("\\n") end def use_tempfile_if_missing(file) tempfile = nil unless File.exists?(file) Chef::Log.debug("file #{file} does not exist to diff against, using empty tempfile") tempfile = Tempfile.new("chef-diff") file = tempfile.path end yield file unless tempfile.nil? tempfile.close tempfile.unlink end end def diff(old_file, new_file) use_tempfile_if_missing(old_file) do |old_file| use_tempfile_if_missing(new_file) do |new_file| @error = do_diff(old_file, new_file) end end end # produces a unified-output-format diff with 3 lines of context # ChefFS uses udiff() directly def udiff(old_file, new_file) diff_str = "" file_length_difference = 0 old_data = IO.readlines(old_file).map { |e| e.chomp } new_data = IO.readlines(new_file).map { |e| e.chomp } diff_data = ::Diff::LCS.diff(old_data, new_data) return diff_str if old_data.empty? && new_data.empty? return "No differences encountered\n" if diff_data.empty? # write diff header (standard unified format) ft = File.stat(old_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') diff_str << "--- #{old_file}\t#{ft}\n" ft = File.stat(new_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') diff_str << "+++ #{new_file}\t#{ft}\n" # loop over diff hunks. if a hunk overlaps with the last hunk, # join them. otherwise, print out the old one. old_hunk = hunk = nil diff_data.each do |piece| begin hunk = ::Diff::LCS::Hunk.new(old_data, new_data, piece, 3, file_length_difference) file_length_difference = hunk.file_length_difference next unless old_hunk next if hunk.merge(old_hunk) diff_str << old_hunk.diff(:unified) << "\n" ensure old_hunk = hunk end end diff_str << old_hunk.diff(:unified) << "\n" return diff_str end private def do_diff(old_file, new_file) if Chef::Config[:diff_disabled] return "(diff output suppressed by config)" end diff_filesize_threshold = Chef::Config[:diff_filesize_threshold] diff_output_threshold = Chef::Config[:diff_output_threshold] if ::File.size(old_file) > diff_filesize_threshold || ::File.size(new_file) > diff_filesize_threshold return "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)" end # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs return "(current file is binary, diff output suppressed)" if is_binary?(old_file) return "(new content is binary, diff output suppressed)" if is_binary?(new_file) begin Chef::Log.debug("running: diff -u #{old_file} #{new_file}") diff_str = udiff(old_file, new_file) rescue Exception => e # Should *not* receive this, but in some circumstances it seems that # an exception can be thrown even using shell_out instead of shell_out! return "Could not determine diff. Error: #{e.message}" end if !diff_str.empty? && diff_str != "No differences encountered\n" if diff_str.length > diff_output_threshold return "(long diff of over #{diff_output_threshold} characters, diff output suppressed)" else diff_str = encode_diff_for_json(diff_str) @diff = diff_str.split("\n") return "(diff available)" end else return "(no diff)" end end def is_binary?(path) File.open(path) do |file| # XXX: this slurps into RAM, but we should have already checked our diff has a reasonable size buff = file.read buff = "" if buff.nil? begin return buff !~ /\A[\s[:print:]]*\z/m rescue ArgumentError => e return true if e.message =~ /invalid byte sequence/ raise end end end def encode_diff_for_json(diff_str) diff_str.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?') end end end end chef-12.3.0/lib/chef/util/windows/0000755000004100000410000000000012520074675016664 5ustar www-datawww-datachef-12.3.0/lib/chef/util/windows/volume.rb0000644000004100000410000000323112520074675020517 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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. # #simple wrapper around Volume APIs. might be possible with WMI, but possibly more complex. require 'chef/util/windows' require 'windows/volume' class Chef::Util::Windows::Volume < Chef::Util::Windows private include Windows::Volume #XXX not defined in the current windows-pr release DeleteVolumeMountPoint = Windows::API.new('DeleteVolumeMountPoint', 'S', 'B') unless defined? DeleteVolumeMountPoint public def initialize(name) name += "\\" unless name =~ /\\$/ #trailing slash required @name = name end def device buffer = 0.chr * 256 if GetVolumeNameForVolumeMountPoint(@name, buffer, buffer.size) return buffer[0,buffer.size].unpack("Z*")[0] else raise ArgumentError, get_last_error end end def delete unless DeleteVolumeMountPoint.call(@name) raise ArgumentError, get_last_error end end def add(args) unless SetVolumeMountPoint(@name, args[:remote]) raise ArgumentError, get_last_error end end end chef-12.3.0/lib/chef/util/windows/net_use.rb0000644000004100000410000000575412520074675020666 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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. # #the Win32 Volume APIs do not support mapping network drives. not supported by WMI either. #see also: WNetAddConnection2 and WNetAddConnection3 #see also cmd.exe: net use /? require 'chef/util/windows' class Chef::Util::Windows::NetUse < Chef::Util::Windows private USE_NOFORCE = 0 USE_FORCE = 1 USE_LOTS_OF_FORCE = 2 #every windows API should support this flag USE_INFO_2 = [ [:local, nil], [:remote, nil], [:password, nil], [:status, 0], [:asg_type, 0], [:refcount, 0], [:usecount, 0], [:username, nil], [:domainname, nil] ] USE_INFO_2_TEMPLATE = USE_INFO_2.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join SIZEOF_USE_INFO_2 = #sizeof(USE_INFO_2) USE_INFO_2.inject(0) do |sum, item| sum + (item[1].class == Fixnum ? 4 : PTR_SIZE) end def use_info_2(args) USE_INFO_2.collect { |field| args.include?(field[0]) ? args[field[0]] : field[1] } end def use_info_2_pack(use) use.collect { |v| v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v)) }.pack(USE_INFO_2_TEMPLATE) end def use_info_2_unpack(buffer) use = Hash.new USE_INFO_2.each_with_index do |field,offset| use[field[0]] = field[1].class == Fixnum ? dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset) end use end public def initialize(localname) @localname = localname @name = multi_to_wide(localname) end def add(args) if args.class == String remote = args args = Hash.new args[:remote] = remote end args[:local] ||= @localname use = use_info_2(args) buffer = use_info_2_pack(use) rc = NetUseAdd.call(nil, 2, buffer, nil) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end def get_info ptr = 0.chr * PTR_SIZE rc = NetUseGetInfo.call(nil, @name, 2, ptr) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end ptr = ptr.unpack('L')[0] buffer = 0.chr * SIZEOF_USE_INFO_2 memcpy(buffer, ptr, buffer.size) NetApiBufferFree(ptr) use_info_2_unpack(buffer) end def device get_info()[:remote] end #XXX should we use some FORCE here? def delete rc = NetUseDel.call(nil, @name, USE_NOFORCE) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end end chef-12.3.0/lib/chef/util/windows/net_user.rb0000644000004100000410000001400612520074675021036 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/windows' require 'chef/exceptions' #wrapper around a subset of the NetUser* APIs. #nothing Chef specific, but not complete enough to be its own gem, so util for now. class Chef::Util::Windows::NetUser < Chef::Util::Windows private LogonUser = Windows::API.new('LogonUser', 'SSSLLP', 'I', 'advapi32') DOMAIN_GROUP_RID_USERS = 0x00000201 UF_SCRIPT = 0x000001 UF_ACCOUNTDISABLE = 0x000002 UF_PASSWD_CANT_CHANGE = 0x000040 UF_NORMAL_ACCOUNT = 0x000200 UF_DONT_EXPIRE_PASSWD = 0x010000 #[:symbol_name, default_val] #default_val duals as field type #array index duals as structure offset #OC-8391 #Changing [:password, nil], to [:password, ""], #if :password is set to nil, windows user creation api ignores the password policy applied #thus initializing it with empty string value. USER_INFO_3 = [ [:name, nil], [:password, ""], [:password_age, 0], [:priv, 0], #"The NetUserAdd and NetUserSetInfo functions ignore this member" [:home_dir, nil], [:comment, nil], [:flags, UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_NORMAL_ACCOUNT], [:script_path, nil], [:auth_flags, 0], [:full_name, nil], [:user_comment, nil], [:parms, nil], [:workstations, nil], [:last_logon, 0], [:last_logoff, 0], [:acct_expires, -1], [:max_storage, -1], [:units_per_week, 0], [:logon_hours, nil], [:bad_pw_count, 0], [:num_logons, 0], [:logon_server, nil], [:country_code, 0], [:code_page, 0], [:user_id, 0], [:primary_group_id, DOMAIN_GROUP_RID_USERS], [:profile, nil], [:home_dir_drive, nil], [:password_expired, 0] ] USER_INFO_3_TEMPLATE = USER_INFO_3.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join SIZEOF_USER_INFO_3 = #sizeof(USER_INFO_3) USER_INFO_3.inject(0){|sum,item| sum + (item[1].class == Fixnum ? 4 : PTR_SIZE) } def user_info_3(args) USER_INFO_3.collect { |field| args.include?(field[0]) ? args[field[0]] : field[1] } end def user_info_3_pack(user) user.collect { |v| v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v)) }.pack(USER_INFO_3_TEMPLATE) end def user_info_3_unpack(buffer) user = Hash.new USER_INFO_3.each_with_index do |field,offset| user[field[0]] = field[1].class == Fixnum ? dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset) end user end def set_info(args) user = user_info_3(args) buffer = user_info_3_pack(user) rc = NetUserSetInfo.call(nil, @name, 3, buffer, nil) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end public def initialize(username) @username = username @name = multi_to_wide(username) end LOGON32_PROVIDER_DEFAULT = 0 LOGON32_LOGON_NETWORK = 3 #XXX for an extra painful alternative, see: http://support.microsoft.com/kb/180548 def validate_credentials(passwd) token = 0.chr * PTR_SIZE res = LogonUser.call(@username, nil, passwd, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, token) if res == 0 return false end ::Windows::Handle::CloseHandle.call(token.unpack('L')[0]) return true end def get_info ptr = 0.chr * PTR_SIZE rc = NetUserGetInfo.call(nil, @name, 3, ptr) if rc == NERR_UserNotFound raise Chef::Exceptions::UserIDNotFound, get_last_error(rc) elsif rc != NERR_Success raise ArgumentError, get_last_error(rc) end ptr = ptr.unpack('L')[0] buffer = 0.chr * SIZEOF_USER_INFO_3 memcpy(buffer, ptr, buffer.size) NetApiBufferFree(ptr) user_info_3_unpack(buffer) end def add(args) user = user_info_3(args) buffer = user_info_3_pack(user) rc = NetUserAdd.call(nil, 3, buffer, rc) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end #usri3_primary_group_id: #"When you call the NetUserAdd function, this member must be DOMAIN_GROUP_RID_USERS" NetLocalGroupAddMembers(nil, multi_to_wide("Users"), 3, buffer[0,PTR_SIZE], 1) end def user_modify(&proc) user = get_info user[:last_logon] = user[:units_per_week] = 0 #ignored as per USER_INFO_3 doc user[:logon_hours] = nil #PBYTE field; \0 == no changes proc.call(user) set_info(user) end def update(args) user_modify do |user| args.each do |key,val| user[key] = val end end end def delete rc = NetUserDel.call(nil, @name) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end def disable_account user_modify do |user| user[:flags] |= UF_ACCOUNTDISABLE #This does not set the password to nil. It (for some reason) means to ignore updating the field. #See similar behavior for the logon_hours field documented at #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx user[:password] = nil end end def enable_account user_modify do |user| user[:flags] &= ~UF_ACCOUNTDISABLE #This does not set the password to nil. It (for some reason) means to ignore updating the field. #See similar behavior for the logon_hours field documented at #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx user[:password] = nil end end def check_enabled (get_info()[:flags] & UF_ACCOUNTDISABLE) != 0 end end chef-12.3.0/lib/chef/util/windows/net_group.rb0000644000004100000410000000605012520074675021214 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/windows' #wrapper around a subset of the NetGroup* APIs. #nothing Chef specific, but not complete enough to be its own gem, so util for now. class Chef::Util::Windows::NetGroup < Chef::Util::Windows private def pack_str(s) [str_to_ptr(s)].pack('L') end def modify_members(members, func) buffer = 0.chr * (members.size * PTR_SIZE) members.each_with_index do |member,offset| buffer[offset*PTR_SIZE,PTR_SIZE] = pack_str(multi_to_wide(member)) end rc = func.call(nil, @name, 3, buffer, members.size) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end public def initialize(groupname) @name = multi_to_wide(groupname) end def local_get_members group_members = [] handle = 0.chr * PTR_SIZE rc = ERROR_MORE_DATA while rc == ERROR_MORE_DATA ptr = 0.chr * PTR_SIZE nread = 0.chr * PTR_SIZE total = 0.chr * PTR_SIZE rc = NetLocalGroupGetMembers.call(nil, @name, 0, ptr, -1, nread, total, handle) if (rc == NERR_Success) || (rc == ERROR_MORE_DATA) ptr = ptr.unpack('L')[0] nread = nread.unpack('i')[0] members = 0.chr * (nread * PTR_SIZE ) #nread * sizeof(LOCALGROUP_MEMBERS_INFO_0) memcpy(members, ptr, members.size) # 1 pointer field in LOCALGROUP_MEMBERS_INFO_0, offset 0 is lgrmi0_sid nread.times do |i| sid_address = members[i * PTR_SIZE, PTR_SIZE].unpack('L')[0] sid_ptr = FFI::Pointer.new(sid_address) member_sid = Chef::ReservedNames::Win32::Security::SID.new(sid_ptr) group_members << member_sid.to_s end NetApiBufferFree(ptr) else raise ArgumentError, get_last_error(rc) end end group_members end def local_add rc = NetLocalGroupAdd.call(nil, 0, pack_str(@name), nil) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end def local_set_members(members) modify_members(members, NetLocalGroupSetMembers) end def local_add_members(members) modify_members(members, NetLocalGroupAddMembers) end def local_delete_members(members) modify_members(members, NetLocalGroupDelMembers) end def local_delete rc = NetLocalGroupDel.call(nil, @name) if rc != NERR_Success raise ArgumentError, get_last_error(rc) end end end chef-12.3.0/lib/chef/util/selinux.rb0000644000004100000410000000553612520074675017217 0ustar www-datawww-data# # Author:: Sean O'Meara # Author:: Kevin Keane # Author:: Lamont Granquist () # # Copyright:: Copyright (c) 2011 Opscode, Inc. # Copyright:: Copyright (c) 2013, North County Tech Center, LLC # # 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/mixin/shell_out' require 'chef/mixin/which' class Chef class Util # # IMPORTANT: We assume that selinux utilities are installed on an # selinux enabled server. Provisioning an selinux enabled server # without selinux utilities is not supported. # module Selinux include Chef::Mixin::ShellOut include Chef::Mixin::Which # We want to initialize below variables once during a # chef-client run therefore they are class variables. @@selinux_enabled = nil @@restorecon_path = nil @@selinuxenabled_path = nil def selinux_enabled? @@selinux_enabled = check_selinux_enabled? if @@selinux_enabled.nil? @@selinux_enabled end def restore_security_context(file_path, recursive = false) if restorecon_path restorecon_command = recursive ? "#{restorecon_path} -R -r" : "#{restorecon_path} -R" restorecon_command += " \"#{file_path}\"" Chef::Log.debug("Restoring selinux security content with #{restorecon_command}") shell_out!(restorecon_command) else Chef::Log.warn "Can not find 'restorecon' on the system. Skipping selinux security context restore." end end private def restorecon_path @@restorecon_path = which("restorecon") if @@restorecon_path.nil? @@restorecon_path end def selinuxenabled_path @@selinuxenabled_path = which("selinuxenabled") if @@selinuxenabled_path.nil? @@selinuxenabled_path end def check_selinux_enabled? if selinuxenabled_path cmd = shell_out!(selinuxenabled_path, :returns => [0,1]) case cmd.exitstatus when 1 return false when 0 return true else raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}" end else # We assume selinux is not enabled if selinux utils are not # installed. return false end end end end end chef-12.3.0/lib/chef/util/threaded_job_queue.rb0000644000004100000410000000310512520074675021334 0ustar www-datawww-data# Copyright:: Copyright (c) 2014 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 'thread' class Chef class Util # A simple threaded job queue # # Create a queue: # # queue = ThreadedJobQueue.new # # Add jobs: # # queue << lambda { |lock| foo.the_bar } # # A job is a callable that optionally takes a Mutex instance as its only # parameter. # # Then start processing jobs with +n+ threads: # # queue.process(n) # class ThreadedJobQueue def initialize @queue = Queue.new @lock = Mutex.new end def <<(job) @queue << job end def process(concurrency = 10) workers = (1..concurrency).map do Thread.new do loop do fn = @queue.pop fn.arity == 1 ? fn.call(@lock) : fn.call end end end workers.each { |worker| self << Thread.method(:exit) } workers.each { |worker| worker.join } end end end end chef-12.3.0/lib/chef/util/backup.rb0000644000004100000410000000551212520074675016767 0ustar www-datawww-data# # Author:: Lamont Granquist () # 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/util/path_helper' class Chef class Util class Backup attr_reader :new_resource attr_accessor :path def initialize(new_resource, path = nil) @new_resource = new_resource @path = path.nil? ? new_resource.path : path end def backup! if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(path) do_backup # Clean up after the number of backups slice_number = @new_resource.backup backup_files = sorted_backup_files if backup_files.length >= @new_resource.backup remainder = backup_files.slice(slice_number..-1) remainder.each do |backup_to_delete| delete_backup(backup_to_delete) end end end end private def backup_filename @backup_filename ||= begin time = Time.now nanoseconds = sprintf("%6f", time.to_f).split('.')[1] savetime = time.strftime("%Y%m%d%H%M%S.#{nanoseconds}") backup_filename = "#{path}.chef-#{savetime}" backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows end end def prefix # if :file_backup_path is nil, we fallback to the old behavior of # keeping the backup in the same directory. We also need to to_s it # so we don't get a type error around implicit to_str conversions. @prefix ||= Chef::Config[:file_backup_path].to_s end def backup_path @backup_path ||= ::File.join(prefix, backup_filename) end def do_backup FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path] FileUtils.cp(path, backup_path, :preserve => true) Chef::Log.info("#{@new_resource} backed up to #{backup_path}") end def delete_backup(backup_file) FileUtils.rm(backup_file) Chef::Log.info("#{@new_resource} removed backup at #{backup_file}") end def sorted_backup_files Dir[Chef::Util::PathHelper.escape_glob(prefix, ".#{path}") + ".chef-*"].sort { |a,b| b <=> a } end end end end chef-12.3.0/lib/chef/util/file_edit.rb0000644000004100000410000000644412520074675017453 0ustar www-datawww-data# # Author:: Nuo Yan () # 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/util/editor' require 'fileutils' class Chef class Util class FileEdit private attr_reader :editor, :original_pathname public def initialize(filepath) raise ArgumentError, "File '#{filepath}' does not exist" unless File.exist?(filepath) @editor = Editor.new(File.open(filepath, &:readlines)) @original_pathname = filepath @file_edited = false end # return if file has been edited def file_edited? @file_edited end #search the file line by line and match each line with the given regex #if matched, replace the whole line with newline. def search_file_replace_line(regex, newline) @changes = (editor.replace_lines(regex, newline) > 0) || @changes end #search the file line by line and match each line with the given regex #if matched, replace the match (all occurrences) with the replace parameter def search_file_replace(regex, replace) @changes = (editor.replace(regex, replace) > 0) || @changes end #search the file line by line and match each line with the given regex #if matched, delete the line def search_file_delete_line(regex) @changes = (editor.remove_lines(regex) > 0) || @changes end #search the file line by line and match each line with the given regex #if matched, delete the match (all occurrences) from the line def search_file_delete(regex) search_file_replace(regex, '') end #search the file line by line and match each line with the given regex #if matched, insert newline after each matching line def insert_line_after_match(regex, newline) @changes = (editor.append_line_after(regex, newline) > 0) || @changes end #search the file line by line and match each line with the given regex #if not matched, insert newline at the end of the file def insert_line_if_no_match(regex, newline) @changes = (editor.append_line_if_missing(regex, newline) > 0) || @changes end def unwritten_changes? !!@changes end #Make a copy of old_file and write new file out (only if file changed) def write_file if @changes backup_pathname = original_pathname + ".old" FileUtils.cp(original_pathname, backup_pathname, :preserve => true) File.open(original_pathname, "w") do |newfile| editor.lines.each do |line| newfile.puts(line) end newfile.flush end @file_edited = true end @changes = false end end end end chef-12.3.0/lib/chef/util/path_helper.rb0000644000004100000410000002140212520074675020011 0ustar www-datawww-data# # Author:: Bryan McLellan # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef class Util class PathHelper # Maximum characters in a standard Windows path (260 including drive letter and NUL) WIN_MAX_PATH = 259 def self.dirname(path) if Chef::Platform.windows? # Find the first slash, not counting trailing slashes end_slash = path.size loop do slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1) if !slash return end_slash == path.size ? '.' : path_separator elsif slash == end_slash - 1 end_slash = slash else return path[0..slash-1] end end else ::File.dirname(path) end end BACKSLASH = '\\'.freeze def self.path_separator if Chef::Platform.windows? File::ALT_SEPARATOR || BACKSLASH else File::SEPARATOR end end def self.join(*args) args.flatten.inject do |joined_path, component| # Joined path ends with / joined_path = joined_path.sub(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+$/, '') component = component.sub(/^[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+/, '') joined_path += "#{path_separator}#{component}" end end def self.validate_path(path) if Chef::Platform.windows? unless printable?(path) msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings." Chef::Log.error(msg) raise Chef::Exceptions::ValidationFailed, msg end if windows_max_length_exceeded?(path) Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'") path.insert(0, "\\\\?\\") end end path end def self.windows_max_length_exceeded?(path) # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx unless path =~ /^\\\\?\\/ if path.length > WIN_MAX_PATH return true end end false end def self.printable?(string) # returns true if string is free of non-printable characters (escape sequences) # this returns false for whitespace escape sequences as well, e.g. \n\t if string =~ /[^[:print:]]/ false else true end end # Produces a comparable path. def self.canonical_path(path, add_prefix=true) # First remove extra separators and resolve any relative paths abs_path = File.absolute_path(path) if Chef::Platform.windows? # Add the \\?\ API prefix on Windows unless add_prefix is false # Downcase on Windows where paths are still case-insensitive abs_path.gsub!(::File::SEPARATOR, path_separator) if add_prefix && abs_path !~ /^\\\\?\\/ abs_path.insert(0, "\\\\?\\") end abs_path.downcase! end abs_path end def self.cleanpath(path) path = Pathname.new(path).cleanpath.to_s # ensure all forward slashes are backslashes if Chef::Platform.windows? path = path.gsub(File::SEPARATOR, path_separator) end path end def self.paths_eql?(path1, path2) canonical_path(path1) == canonical_path(path2) end # Paths which may contain glob-reserved characters need # to be escaped before globbing can be done. # http://stackoverflow.com/questions/14127343 def self.escape_glob(*parts) path = cleanpath(join(*parts)) path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x } end def self.relative_path_from(from, to) pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from))) end # Retrieves the "home directory" of the current user while trying to ascertain the existence # of said directory. The path returned uses / for all separators (the ruby standard format). # If the home directory doesn't exist or an error is otherwise encountered, nil is returned. # # If a set of path elements is provided, they are appended as-is to the home path if the # homepath exists. # # If an optional block is provided, the joined path is passed to that block if the home path is # valid and the result of the block is returned instead. # # Home-path discovery is performed once. If a path is discovered, that value is memoized so # that subsequent calls to home_dir don't bounce around. # # See self.all_homes. def self.home(*args) @@home_dir ||= self.all_homes { |p| break p } if @@home_dir path = File.join(@@home_dir, *args) block_given? ? (yield path) : path end end # See self.home. This method performs a similar operation except that it yields all the different # possible values of 'HOME' that one could have on this platform. Hence, on windows, if # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice. # This method goes out and checks the existence of each location at the time of the call. # # The return is a list of all the returned values from each block invocation or a list of paths # if no block is provided. def self.all_homes(*args) paths = [] if Chef::Platform.windows? # By default, Ruby uses the the following environment variables to determine Dir.home: # HOME # HOMEDRIVE HOMEPATH # USERPROFILE # Ruby only checks to see if the variable is specified - not if the directory actually exists. # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive) # while USERPROFILE points to the location where the user application settings and profile are stored. HOME # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is # HOMESHARE instead of HOMEDRIVE. # # We instead walk down the following and only include paths that actually exist. # HOME # HOMEDRIVE HOMEPATH # HOMESHARE HOMEPATH # USERPROFILE paths << ENV['HOME'] paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH'] paths << ENV['USERPROFILE'] end paths << Dir.home if ENV['HOME'] # Depending on what environment variables we're using, the slashes can go in any which way. # Just change them all to / to keep things consistent. # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on # the particular brand of kool-aid you consume. This code assumes that \ and / are both # path separators on any system being used. paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path } # Filter out duplicate paths and paths that don't exist. valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) } valid_paths = valid_paths.uniq # Join all optional path elements at the end. # If a block is provided, invoke it - otherwise just return what we've got. joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) } if block_given? joined_paths.each { |p| yield p } else joined_paths end end end end end # Break a require loop when require chef/util/path_helper require 'chef/platform' require 'chef/exceptions' chef-12.3.0/lib/chef/util/powershell/0000755000004100000410000000000012520074675017356 5ustar www-datawww-datachef-12.3.0/lib/chef/util/powershell/ps_credential.rb0000644000004100000410000000213712520074675022522 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: Copyright (c) 2015 Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/win32/crypto' if Chef::Platform.windows? class Chef::Util::Powershell class PSCredential def initialize(username, password) @username = username @password = password end def to_psobject "New-Object System.Management.Automation.PSCredential('#{@username}',('#{encrypt(@password)}' | ConvertTo-SecureString))" end private def encrypt(str) Chef::ReservedNames::Win32::Crypto.encrypt(str) end end end chef-12.3.0/lib/chef/util/powershell/cmdlet_result.rb0000644000004100000410000000253612520074675022557 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: Copyright (c) 2014 Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/json_compat' class Chef class Util class Powershell class CmdletResult attr_reader :output_format def initialize(status, streams, output_format) @status = status @output_format = output_format @streams = streams end def stdout @status.stdout end def stderr @status.stderr end def stream(name) @streams[name].read end def return_value if output_format == :object Chef::JSONCompat.parse(stream(:json)) elsif output_format == :json stream(:json) else @status.stdout end end def succeeded? @succeeded = @status.status.exitstatus == 0 end end end end end chef-12.3.0/lib/chef/util/powershell/cmdlet.rb0000644000004100000410000001224012520074675021152 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'mixlib/shellout' require 'chef/mixin/windows_architecture_helper' require 'chef/util/powershell/cmdlet_result' class Chef class Util class Powershell class Cmdlet def initialize(node, cmdlet, output_format=nil, output_format_options={}) @output_format = output_format @node = node case output_format when nil @json_format = false when :json @json_format = true when :text @json_format = false when :object @json_format = true else raise ArgumentError, "Invalid output format #{output_format.to_s} specified" end @cmdlet = cmdlet @output_format_options = output_format_options end attr_reader :output_format def run(switches={}, execution_options={}, *arguments) streams = { :json => CmdletStream.new('json'), :verbose => CmdletStream.new('verbose'), } arguments_string = arguments.join(' ') switches_string = command_switches_string(switches) json_depth = 5 if @json_format && @output_format_options.has_key?(:depth) json_depth = @output_format_options[:depth] end json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\ "> #{streams[:json].path}" : "" redirections = "4> '#{streams[:verbose].path}'" command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\ "-command \"trap [Exception] {write-error -exception "\ "($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} "\ "#{arguments_string} #{redirections}"\ "#{json_command}\";if ( ! $? ) { exit 1 }" augmented_options = {:returns => [0], :live_stream => false}.merge(execution_options) command = Mixlib::ShellOut.new(command_string, augmented_options) status = nil with_os_architecture(@node) do status = command.run_command end CmdletResult.new(status, streams, @output_format) end def run!(switches={}, execution_options={}, *arguments) result = run(switches, execution_options, arguments) if ! result.succeeded? raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{result.stderr}" end result end protected include Chef::Mixin::WindowsArchitectureHelper def validate_switch_name!(switch_parameter_name) if !!(switch_parameter_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name" end end def escape_parameter_value(parameter_value) parameter_value.gsub(/(`|'|"|#)/,'`\1') end def escape_string_parameter_value(parameter_value) "'#{escape_parameter_value(parameter_value)}'" end def command_switches_string(switches) command_switches = switches.map do | switch_name, switch_value | if switch_name.class != Symbol raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name.to_s}'. The switch must be specified as a Symbol'" end validate_switch_name!(switch_name) switch_argument = '' switch_present = true case switch_value when Numeric switch_argument = switch_value.to_s when Float switch_argument = switch_value.to_s when FalseClass switch_present = false when TrueClass when String switch_argument = escape_string_parameter_value(switch_value) else raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name.to_s}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`" end switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(' ').strip : '' end command_switches.join(' ') end class CmdletStream def initialize(name) @filename = Dir::Tmpname.create(name) {} ObjectSpace.define_finalizer(self, self.class.destroy(@filename)) end def path @filename end def read if File.exist? @filename File.open(@filename, 'rb:bom|UTF-16LE') do |f| f.read.encode('UTF-8') end end end def self.destroy(name) proc { File.delete(name) if File.exists? name } end end end end end end chef-12.3.0/lib/chef/util/windows.rb0000644000004100000410000000317412520074675017216 0ustar www-datawww-data# # Author:: Doug MacEachern () # Copyright:: Copyright (c) 2010 VMware, 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. # #requires: gem install windows-pr require 'windows/api' require 'windows/error' require 'windows/handle' require 'windows/unicode' require 'windows/msvcrt/buffer' require 'windows/msvcrt/string' require 'windows/network/management' class Chef class Util class Windows protected include ::Windows::Error include ::Windows::Unicode include ::Windows::MSVCRT::Buffer include ::Windows::MSVCRT::String include ::Windows::Network::Management PTR_SIZE = 4 #XXX 64-bit def lpwstr_to_s(buffer, offset) str = 0.chr * (256 * 2) #XXX unhardcode this length (*2 for WCHAR) wcscpy str, buffer[offset*PTR_SIZE,PTR_SIZE].unpack('L')[0] wide_to_multi str end def dword_to_i(buffer, offset) buffer[offset*PTR_SIZE,PTR_SIZE].unpack('i')[0] || 0 end #return pointer for use with pack('L') def str_to_ptr(v) [v].pack('p*').unpack('L')[0] end end end end chef-12.3.0/lib/chef/util/dsc/0000755000004100000410000000000012520074675015743 5ustar www-datawww-datachef-12.3.0/lib/chef/util/dsc/resource_info.rb0000644000004100000410000000115312520074675021132 0ustar www-datawww-data class Chef class Util class DSC class ResourceInfo # The name is the text following [Start Set] attr_reader :name # A list of all log messages between [Start Set] and [End Set]. # Each line is an element in the list. attr_reader :change_log def initialize(name, sets, change_log) @name = name @sets = sets @change_log = change_log || [] end # Does this resource change the state of the system? def changes_state? @sets end end end end end chef-12.3.0/lib/chef/util/dsc/lcm_output_parser.rb0000644000004100000410000001143112520074675022037 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/log' require 'chef/util/dsc/resource_info' require 'chef/exceptions' class Chef class Util class DSC class LocalConfigurationManager module Parser # Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects # that describe how the resources affected the system # # Example: # parse <<-EOF # What if: [Machine]: LCM: [Start Set ] # What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere] # What if: [Machine]: LCM: [Start Set ] [[File]FileToNotBeThere] # What if: [C:\ShouldNotExist.txt] removed # What if: [Machine]: LCM: [End Set ] [[File]FileToNotBeThere] in 0.1 seconds # What if: [Machine]: LCM: [End Resource ] [[File]FileToNotBeThere] # What if: [Machine]: LCM: [End Set ] # EOF # # would return # # [ # Chef::Util::DSC::ResourceInfo.new( # '[[File]FileToNotBeThere]', # true, # [ # '[[File]FileToNotBeThere]', # '[C:\Shouldnotexist.txt]', # '[[File]FileToNotBeThere] in 0.1 seconds' # ] # ) # ] # def self.parse(lcm_output) lcm_output ||= "" current_resource = Hash.new resources = [] lcm_output.lines.each do |line| op_action, op_type, info = parse_line(line) case op_action when :start case op_type when :set if current_resource[:name] current_resource[:context] = :logging current_resource[:logs] = [info] end when :resource if current_resource[:name] resources.push(current_resource) end current_resource = {:name => info} else Chef::Log.debug("Ignoring op_action #{op_action}: Read line #{line}") end when :end # Make sure we log the last line if current_resource[:context] == :logging and info.include? current_resource[:name] current_resource[:logs].push(info) end current_resource[:context] = nil when :skip current_resource[:skipped] = true when :info if current_resource[:context] == :logging current_resource[:logs].push(info) end end end if current_resource[:name] resources.push(current_resource) end if resources.length > 0 build_resource_info(resources) else raise Chef::Exceptions::LCMParser, "Could not parse:\n#{lcm_output}" end end def self.parse_line(line) if match = line.match(/^.*?:.*?:\s*LCM:\s*\[(.*?)\](.*)/) # If the line looks like # What If: [machinename]: LCM: [op_action op_type] message # extract op_action, op_type, and message operation, info = match.captures op_action, op_type = operation.strip.split(' ').map {|m| m.downcase.to_sym} else op_action = op_type = :info if match = line.match(/^.*?:.*?: \s+(.*)/) info = match.captures[0] else info = line end end info.strip! # Because this was formatted for humans return [op_action, op_type, info] end private_class_method :parse_line def self.build_resource_info(resources) resources.map do |r| Chef::Util::DSC::ResourceInfo.new(r[:name], !r[:skipped], r[:logs]) end end private_class_method :build_resource_info end end end end end chef-12.3.0/lib/chef/util/dsc/configuration_generator.rb0000644000004100000410000001211712520074675023207 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/cmdlet' class Chef::Util::DSC class ConfigurationGenerator def initialize(node, config_directory) @node = node @config_directory = config_directory end def configuration_document_from_script_code(code, configuration_flags, imports, shellout_flags) Chef::Log.debug("DSC: DSC code:\n '#{code}'") generated_script_path = write_document_generation_script(code, 'chef_dsc', imports) begin configuration_document_from_script_path(generated_script_path, 'chef_dsc', configuration_flags, shellout_flags) ensure ::FileUtils.rm(generated_script_path) end end def configuration_document_from_script_path(script_path, configuration_name, configuration_flags, shellout_flags) validate_configuration_name!(configuration_name) document_generation_cmdlet = Chef::Util::Powershell::Cmdlet.new( @node, configuration_document_generation_code(script_path, configuration_name)) merged_configuration_flags = get_merged_configuration_flags!(configuration_flags, configuration_name) document_generation_cmdlet.run!(merged_configuration_flags, shellout_flags) configuration_document_location = find_configuration_document(configuration_name) if ! configuration_document_location raise RuntimeError, "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script" end configuration_document = get_configuration_document(configuration_document_location) ::FileUtils.rm_rf(configuration_document_location) configuration_document end protected # From PowerShell error help for the Configuration language element: # Standard names may only contain letters (a-z, A-Z), numbers (0-9), and underscore (_). # The name may not be null or empty, and should start with a letter. def validate_configuration_name!(configuration_name) if !!(configuration_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false raise ArgumentError, 'Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name' end end def get_merged_configuration_flags!(configuration_flags, configuration_name) merged_configuration_flags = { :outputpath => configuration_document_directory(configuration_name) } if configuration_flags configuration_flags.map do | switch, value | if merged_configuration_flags.key?(switch.to_s.downcase.to_sym) raise ArgumentError, "The `flags` attribute for the dsc_script resource contained a command line switch :#{switch.to_s} that is disallowed." end merged_configuration_flags[switch.to_s.downcase.to_sym] = value end end merged_configuration_flags end def configuration_code(code, configuration_name, imports) <<-EOF $ProgressPreference = 'SilentlyContinue'; Configuration '#{configuration_name}' { #{generate_import_resource_statements(imports).join(" \n")} node 'localhost' { #{code} } } EOF end def generate_import_resource_statements(imports) if imports imports.map do |resource_module, resources| if resources.length == 0 || resources.include?('*') "Import-DscResource -ModuleName #{resource_module}" else "Import-DscResource -ModuleName #{resource_module} -Name #{resources.join(',')}" end end else [] end end def configuration_document_generation_code(configuration_script, configuration_name) ". '#{configuration_script}';#{configuration_name}" end def write_document_generation_script(code, configuration_name, imports) script_path = "#{@config_directory}/chef_dsc_config.ps1" ::File.open(script_path, 'wt') do | script | script.write(configuration_code(code, configuration_name, imports)) end script_path end def find_configuration_document(configuration_name) document_directory = configuration_document_directory(configuration_name) document_file_name = ::Dir.entries(document_directory).find { | path | path =~ /.*.mof/ } ::File.join(document_directory, document_file_name) if document_file_name end def configuration_document_directory(configuration_name) ::File.join(@config_directory, configuration_name) end def get_configuration_document(document_path) ::File.open(document_path, 'rb') do | file | file.read end end end end chef-12.3.0/lib/chef/util/dsc/local_configuration_manager.rb0000644000004100000410000001221012520074675023777 0ustar www-datawww-data# # Author:: Adam Edwards () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/cmdlet' require 'chef/util/dsc/lcm_output_parser' class Chef::Util::DSC class LocalConfigurationManager def initialize(node, configuration_path) @node = node @configuration_path = configuration_path clear_execution_time end def test_configuration(configuration_document, shellout_flags) status = run_configuration_cmdlet(configuration_document, false, shellout_flags) log_what_if_exception(status.stderr) unless status.succeeded? configuration_update_required?(status.return_value) end def set_configuration(configuration_document, shellout_flags) run_configuration_cmdlet(configuration_document, true, shellout_flags) end def last_operation_execution_time_seconds if @operation_start_time && @operation_end_time @operation_end_time - @operation_start_time end end private def run_configuration_cmdlet(configuration_document, apply_configuration, shellout_flags) Chef::Log.debug("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.") test_only_parameters = ! apply_configuration ? '-whatif; if (! $?) { exit 1 }' : '' start_operation_timing command_code = lcm_command_code(@configuration_path, test_only_parameters) status = nil begin save_configuration_document(configuration_document) cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, "#{command_code}") if apply_configuration status = cmdlet.run!({}, shellout_flags) else status = cmdlet.run({}, shellout_flags) end ensure end_operation_timing remove_configuration_document if last_operation_execution_time_seconds Chef::Log.debug("DSC: DSC operation completed in #{last_operation_execution_time_seconds} seconds.") end end Chef::Log.debug("DSC: Completed call to DSC Local Config Manager") status end def lcm_command_code(configuration_path, test_only_parameters) <<-EOH $ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -erroraction 'continue' -force #{test_only_parameters} EOH end def log_what_if_exception(what_if_exception_output) if whatif_not_supported?(what_if_exception_output) # LCM returns an error if any of the resources do not support the opptional What-If Chef::Log::warn("Received error while testing configuration due to resource not supporting 'WhatIf'") elsif dsc_module_import_failure?(what_if_exception_output) Chef::Log::warn("Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{what_if_exception_output.gsub(/\s+/, ' ')}") else Chef::Log::warn("Received error while testing configuration:\n#{what_if_exception_output.gsub(/\s+/, ' ')}") end end def whatif_not_supported?(what_if_exception_output) !! (what_if_exception_output.gsub(/[\r\n]+/, '').gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/i) end def dsc_module_import_failure?(what_if_output) !! (what_if_output =~ /\sCimException/ && what_if_output =~ /ProviderOperationExecutionFailure/ && what_if_output =~ /\smodule\s+is\s+installed/) end def configuration_update_required?(what_if_output) Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}") begin Parser::parse(what_if_output) rescue Chef::Exceptions::LCMParser => e Chef::Log::warn("Could not parse LCM output: #{e}") [Chef::Util::DSC::ResourceInfo.new('Unknown DSC Resources', true, ['Unknown changes because LCM output was not parsable.'])] end end def save_configuration_document(configuration_document) ::FileUtils.mkdir_p(@configuration_path) ::File.open(configuration_document_path, 'wb') do | file | file.write(configuration_document) end end def remove_configuration_document ::FileUtils.rm(configuration_document_path) end def configuration_document_path File.join(@configuration_path,'..mof') end def clear_execution_time @operation_start_time = nil @operation_end_time = nil end def start_operation_timing clear_execution_time @operation_start_time = Time.now end def end_operation_timing @operation_end_time = Time.now end end end chef-12.3.0/lib/chef/util/dsc/resource_store.rb0000644000004100000410000000520412520074675021334 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: Copyright (c) 2015 Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/util/powershell/cmdlet' require 'chef/util/powershell/cmdlet_result' require 'chef/exceptions' class Chef class Util class DSC class ResourceStore def self.instance @@instance ||= ResourceStore.new.tap do |store| store.send(:populate_cache) end end def resources @resources ||= [] end def find(name, module_name=nil) found = find_resources(name, module_name, resources) # We don't have it, query for the resource...it might # have been added since we last queried if found.length == 0 rs = query_resource(name) add_resources(rs) found = find_resources(name, module_name, rs) end found end private def add_resource(new_r) count = resources.count do |r| r['ResourceType'].casecmp(new_r['ResourceType']) == 0 end if count == 0 resources << new_r end end def add_resources(rs) rs.each do |r| add_resource(r) end end def populate_cache @resources = query_resources end def find_resources(name, module_name, rs) found = rs.find_all do |r| name_matches = r['Name'].casecmp(name) == 0 if name_matches module_name == nil || (r['Module'] and r['Module']['Name'].casecmp(module_name) == 0) else false end end end # Returns a list of dsc resources def query_resources cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, 'get-dscresource', :object) result = cmdlet.run result.return_value end # Returns a list of dsc resources matching the provided name def query_resource(resource_name) cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource #{resource_name}", :object) result = cmdlet.run ret_val = result.return_value if ret_val.nil? [] elsif ret_val.is_a? Array ret_val else [ret_val] end end end end end end chef-12.3.0/lib/chef/util/editor.rb0000644000004100000410000000374112520074675017012 0ustar www-datawww-data# # Author:: Chris Bandy () # Copyright:: Copyright (c) 2014 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. # class Chef class Util class Editor attr_reader :lines def initialize(lines) @lines = lines.to_a.clone end def append_line_after(search, line_to_append) lines = [] @lines.each do |line| lines << line lines << line_to_append if line.match(search) end (lines.length - @lines.length).tap { @lines = lines } end def append_line_if_missing(search, line_to_append) count = 0 unless @lines.find { |line| line.match(search) } count = 1 @lines << line_to_append end count end def remove_lines(search) count = 0 @lines.delete_if do |line| count += 1 if line.match(search) end count end def replace(search, replace) count = 0 @lines.map! do |line| if line.match(search) count += 1 line.gsub!(search, replace) else line end end count end def replace_lines(search, replace) count = 0 @lines.map! do |line| if line.match(search) count += 1 replace else line end end count end end end end chef-12.3.0/lib/chef/request_id.rb0000644000004100000410000000175112520074675016712 0ustar www-datawww-data# Author:: Prajakta Purohit () # Copyright:: Copyright (c) 2009, 2010, 2013, 2014 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 'securerandom' require 'singleton' class Chef class RequestID include Singleton def reset_request_id @request_id = nil end def request_id @request_id ||= generate_request_id end def generate_request_id SecureRandom.uuid end end end chef-12.3.0/lib/chef/sandbox.rb0000644000004100000410000000162412520074675016203 0ustar www-datawww-dataclass Chef class Sandbox # I DO NOTHING!! # So, the reason we have a completely empty class here is so that # Chef 11 clients do not choke when interacting with Chef 10 # servers. The original Chef::Sandbox class (that actually did # things) has been removed since its functionality is no longer # needed for Chef 11. However, since we still use the JSON gem # and make use of its "auto-inflation" of classes (driven by the # contents of the 'json_class' key in all of our JSON), any # sandbox responses from a Chef 10 server to a Chef 11 client # would cause knife to crash. The JSON gem would attempt to # auto-inflate based on a "json_class": "Chef::Sandbox" hash # entry, but would not be able to find a Chef::Sandbox class! # # This is a workaround until such time as we can completely remove # the reliance on the "json_class" field. end end chef-12.3.0/lib/chef/file_access_control.rb0000644000004100000410000000451512520074675020547 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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/log' class Chef # == Chef::FileAccessControl # FileAccessControl objects set the owner, group and mode of +file+ to # the values specified by a value object, usually a Chef::Resource. class FileAccessControl if RUBY_PLATFORM =~ /mswin|mingw|windows/ require 'chef/file_access_control/windows' include FileAccessControl::Windows else require 'chef/file_access_control/unix' include FileAccessControl::Unix end attr_reader :current_resource attr_reader :resource attr_reader :provider attr_reader :file # FileAccessControl objects set the owner, group and mode of +file+ to # the values specified by +resource+. +file+ is completely independent # of any file or path attribute on +resource+, so it is possible to set # access control settings on a tempfile (for example). # === Arguments: # resource: probably a Chef::Resource::File object (or subclass), but # this is not required. Must respond to +owner+, +group+, # and +mode+ # file: The file whose access control settings you wish to modify, # given as a String. # # TODO requiring current_resource will break cookbook_file template_file def initialize(current_resource, new_resource, provider) @current_resource, @resource, @provider = current_resource, new_resource, provider @file = @current_resource.path @modified = false end def modified? @modified end private def modified @modified = true end def log_string @resource || @file end end end chef-12.3.0/lib/chef/file_cache.rb0000644000004100000410000001446412520074675016615 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/mixin/params_validate' require 'chef/mixin/create_path' require 'chef/exceptions' require 'chef/json_compat' require 'fileutils' require 'chef/util/path_helper' class Chef class FileCache class << self include Chef::Mixin::ParamsValidate include Chef::Mixin::CreatePath # Write a file to the File Cache. # # === Parameters # path:: The path to the file you want to put in the cache - should # be relative to file_cache_path # contents:: A string with the contents you want written to the file # perm:: Sets file permission bits. Permission bits are platform # dependent; on Unix systems, see open(2) for details. # # === Returns # true def store(path, contents, perm=0640) validate( { :path => path, :contents => contents }, { :path => { :kind_of => String }, :contents => { :kind_of => String }, } ) file_path_array = File.split(path) file_name = file_path_array.pop cache_path = create_cache_path(File.join(file_path_array)) File.open(File.join(cache_path, file_name), "w", perm) do |io| io.print(contents) end true end # Move a file into the cache. Useful with the REST raw file output. # # === Parameters # file:: The path to the file you want in the cache # path:: The relative name you want the new file to use def move_to(file, path) validate( { :file => file, :path => path }, { :file => { :kind_of => String }, :path => { :kind_of => String }, } ) file_path_array = File.split(path) file_name = file_path_array.pop if File.exists?(file) && File.writable?(file) FileUtils.mv( file, File.join(create_cache_path(File.join(file_path_array), true), file_name) ) else raise RuntimeError, "Cannot move #{file} to #{path}!" end end # Read a file from the File Cache # # === Parameters # path:: The path to the file you want to load - should # be relative to file_cache_path # read:: Whether to return the file contents, or the path. # Defaults to true. # # === Returns # String:: A string with the file contents, or the path to the file. # # === Raises # Chef::Exceptions::FileNotFound:: If it cannot find the file in the cache def load(path, read=true) validate( { :path => path }, { :path => { :kind_of => String } } ) cache_path = create_cache_path(path, false) raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path) if read File.read(cache_path) else cache_path end end # Delete a file from the File Cache # # === Parameters # path:: The path to the file you want to delete - should # be relative to file_cache_path # # === Returns # true def delete(path) validate( { :path => path }, { :path => { :kind_of => String }, } ) cache_path = create_cache_path(path, false) if File.exists?(cache_path) File.unlink(cache_path) end true end # List all the files in the Cache # # === Returns # Array:: An array of files in the cache, suitable for use with load, delete and store def list find("**#{File::Separator}*") end ## # Find files in the cache by +glob_pattern+ # === Returns # [String] - An array of file cache keys matching the glob def find(glob_pattern) keys = Array.new Dir[File.join(Chef::Util::PathHelper.escape_glob(file_cache_path), glob_pattern)].each do |f| if File.file?(f) keys << f[/^#{Regexp.escape(Dir[Chef::Util::PathHelper.escape_glob(file_cache_path)].first) + File::Separator}(.+)/, 1] end end keys end # Whether or not this file exists in the Cache # # === Parameters # path:: The path to the file you want to check - is relative # to file_cache_path # # === Returns # True:: If the file exists # False:: If it does not def has_key?(path) validate( { :path => path }, { :path => { :kind_of => String }, } ) full_path = create_cache_path(path, false) if File.exists?(full_path) true else false end end # Create a full path to a given file in the cache. By default, # also creates the path if it does not exist. # # === Parameters # path:: The path to create, relative to file_cache_path # create_if_missing:: True by default - whether to create the path if it does not exist # # === Returns # String:: The fully expanded path def create_cache_path(path, create_if_missing=true) cache_dir = File.expand_path(File.join(file_cache_path, path)) if create_if_missing create_path(cache_dir) else cache_dir end end private def file_cache_path Chef::Config[:file_cache_path] end end end end chef-12.3.0/lib/chef/run_list.rb0000644000004100000410000001114012520074675016376 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Nuo Yan () # Author:: Tim Hinderliter () # Author:: Christopher Walters () # Author:: Seth Falcon () # Copyright:: Copyright (c) 2008-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 'chef/run_list/run_list_item' require 'chef/run_list/run_list_expansion' require 'chef/run_list/versioned_recipe_list' require 'chef/mixin/params_validate' class Chef class RunList include Enumerable include Chef::Mixin::ParamsValidate # @run_list_items is an array of RunListItems that describe the items to # execute in order. RunListItems can load from and convert to the string # forms users set on roles and nodes. # For example: # @run_list_items = ['recipe[foo::bar]', 'role[webserver]'] # Thus, # self.role_names would return ['webserver'] # self.recipe_names would return ['foo::bar'] attr_reader :run_list_items # For backwards compat alias :run_list :run_list_items def initialize(*run_list_items) @run_list_items = run_list_items.map { |i| coerce_to_run_list_item(i) } end def role_names @run_list_items.inject([]){|memo, run_list_item| memo << run_list_item.name if run_list_item.role? ; memo} end alias :roles :role_names def recipe_names @run_list_items.inject([]){|memo, run_list_item| memo << run_list_item.name if run_list_item.recipe? ; memo} end alias :recipes :recipe_names # Add an item of the form "recipe[foo::bar]" or "role[webserver]"; # takes a String or a RunListItem def <<(run_list_item) run_list_item = coerce_to_run_list_item(run_list_item) @run_list_items << run_list_item unless @run_list_items.include?(run_list_item) self end alias :push :<< alias :add :<< def ==(other) if other.kind_of?(Chef::RunList) other.run_list_items == @run_list_items else return false unless other.respond_to?(:size) && (other.size == @run_list_items.size) other_run_list_items = other.dup other_run_list_items.map! { |item| coerce_to_run_list_item(item) } other_run_list_items == @run_list_items end end def to_s @run_list_items.join(", ") end def for_json to_a.map { |item| item.to_s } end def to_json(*a) Chef::JSONCompat.to_json(for_json, *a) end def empty? @run_list_items.length == 0 ? true : false end def [](pos) @run_list_items[pos] end def []=(pos, item) @run_list_items[pos] = parse_entry(item) end def each(&block) @run_list_items.each { |i| block.call(i) } end def each_index(&block) @run_list_items.each_index { |i| block.call(i) } end def include?(item) @run_list_items.include?(parse_entry(item)) end def reset!(*args) @run_list_items.clear args.flatten.each do |item| if item.kind_of?(Chef::RunList) item.each { |r| self << r } else self << item end end self end def remove(item) @run_list_items.delete_if{|i| i == item} self end alias :delete :remove # Expands this run_list: recursively expand roles into their included # recipes. # Returns a RunListExpansion object. def expand(environment, data_source='server', expansion_opts={}) expansion = expansion_for_data_source(environment, data_source, expansion_opts) expansion.expand expansion end # Converts a string run list entry to a RunListItem object. def parse_entry(entry) RunListItem.new(entry) end def coerce_to_run_list_item(item) item.kind_of?(RunListItem) ? item : parse_entry(item) end def expansion_for_data_source(environment, data_source, opts={}) case data_source.to_s when 'disk' RunListExpansionFromDisk.new(environment, @run_list_items) when 'server' RunListExpansionFromAPI.new(environment, @run_list_items, opts[:rest]) end end end end chef-12.3.0/lib/chef/provider_resolver.rb0000644000004100000410000001155112520074675020320 0ustar www-datawww-data# # Author:: Richard Manyanza () # Copyright:: Copyright (c) 2014 Richard Manyanza. # 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 'chef/platform/provider_priority_map' class Chef class ProviderResolver attr_reader :node attr_reader :resource attr_reader :action def initialize(node, resource, action) @node = node @resource = resource @action = action end # return a deterministically sorted list of Chef::Provider subclasses def providers @providers ||= Chef::Provider.descendants end def resolve maybe_explicit_provider(resource) || maybe_dynamic_provider_resolution(resource, action) || maybe_chef_platform_lookup(resource) end # this cut looks at if the provider can handle the resource type on the node def enabled_handlers @enabled_handlers ||= providers.select do |klass| # NB: this is different from resource_resolver which must pass a resource_name # FIXME: deprecate this and normalize on passing resource_name here klass.provides?(node, resource) end.sort {|a,b| a.to_s <=> b.to_s } end # this cut looks at if the provider can handle the specific resource and action def supported_handlers @supported_handlers ||= enabled_handlers.select do |klass| klass.supports?(resource, action) end end private # if resource.provider is set, just return one of those objects def maybe_explicit_provider(resource) return nil unless resource.provider resource.provider end # try dynamically finding a provider based on querying the providers to see what they support def maybe_dynamic_provider_resolution(resource, action) # log this so we know what providers will work for the generic resource on the node (early cut) Chef::Log.debug "providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}" # what providers were excluded by machine state (late cut) Chef::Log.debug "providers that refused resource #{resource} were: #{enabled_handlers - supported_handlers}" Chef::Log.debug "providers that support resource #{resource} include: #{supported_handlers}" # if none of the providers specifically support the resource, we still need to pick one of the providers that are # enabled on the node to handle the why-run use case. handlers = supported_handlers.empty? ? enabled_handlers : supported_handlers Chef::Log.debug "no providers supported the resource, falling back to enabled handlers" if supported_handlers.empty? if handlers.count >= 2 # this magic stack ranks the providers by where they appear in the provider_priority_map, it is mostly used # to pick amongst N different ways to start init scripts on different debian/ubuntu systems. priority_list = [ get_priority_array(node, resource.resource_name) ].flatten.compact handlers = handlers.sort_by { |x| i = priority_list.index x; i.nil? ? Float::INFINITY : i } if priority_list.index(handlers.first).nil? # if we had more than one and we picked one with a precidence of infinity that means that the resource_priority_map # entry for this resource is missing -- we should probably raise here and force resolution of the ambiguity. Chef::Log.warn "Ambiguous provider precedence: #{handlers}, please use Chef.set_provider_priority_array to provide determinism" end handlers = [ handlers.first ] end Chef::Log.debug "providers that survived replacement include: #{handlers}" raise Chef::Exceptions::AmbiguousProviderResolution.new(resource, handlers) if handlers.count >= 2 Chef::Log.debug "dynamic provider resolver FAILED to resolve a provider" if handlers.empty? return nil if handlers.empty? handlers[0] end # try the old static lookup of providers by platform def maybe_chef_platform_lookup(resource) Chef::Platform.find_provider_for_node(node, resource) end # dep injection hooks def get_priority_array(node, resource_name) provider_priority_map.get_priority_array(node, resource_name) end def provider_priority_map Chef::Platform::ProviderPriorityMap.instance end end end chef-12.3.0/lib/chef/applications.rb0000644000004100000410000000020412520074675017224 0ustar www-datawww-datarequire 'chef/application/client' require 'chef/application/knife' require 'chef/application/solo' require 'chef/application/apply' chef-12.3.0/lib/chef/nil_argument.rb0000644000004100000410000000005312520074675017224 0ustar www-datawww-dataclass Chef NIL_ARGUMENT = Object.new end chef-12.3.0/lib/chef/api_client/0000755000004100000410000000000012520074675016324 5ustar www-datawww-datachef-12.3.0/lib/chef/api_client/registration.rb0000644000004100000410000001302712520074675021366 0ustar www-datawww-data# # Author:: Daniel DeLeo () # 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/config' require 'chef/rest' require 'chef/exceptions' class Chef class ApiClient # ==Chef::ApiClient::Registration # Manages the process of creating or updating a Chef::ApiClient on the # server and writing the resulting private key to disk. Registration uses # the validator credentials for its API calls. This allows it to bootstrap # a new client/node identity by borrowing the validator client identity # when creating a new client. class Registration attr_reader :destination attr_reader :name def initialize(name, destination, http_api: nil) @name = name @destination = destination @http_api = http_api @server_generated_private_key = nil end # Runs the client registration process, including creating the client on # the chef-server and writing its private key to disk. #-- # If client creation fails with a 5xx, it is retried up to 5 times. These # retries are on top of the retries with randomized exponential backoff # built in to Chef::REST. The retries here are a workaround for failures # caused by resource contention in Hosted Chef when creating a very large # number of clients simultaneously, (e.g., spinning up 100s of ec2 nodes # at once). Future improvements to the affected component should make # these retries unnecessary. def run assert_destination_writable! retries = Config[:client_registration_retries] || 5 begin create_or_update rescue Net::HTTPFatalError => e # HTTPFatalError implies 5xx. raise if retries <= 0 retries -= 1 Chef::Log.warn("Failed to register new client, #{retries} tries remaining") Chef::Log.warn("Response: HTTP #{e.response.code} - #{e}") retry end write_key end def assert_destination_writable! if (File.exists?(destination) && !File.writable?(destination)) or !File.writable?(File.dirname(destination)) raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?" end end def write_key ::File.open(destination, file_flags, 0600) do |f| f.print(private_key) end rescue IOError => e raise Chef::Exceptions::CannotWritePrivateKey, "Error writing private key to #{destination}: #{e}" end def create_or_update create rescue Net::HTTPServerException => e # If create fails because the client exists, attempt to update. This # requires admin privileges. raise unless e.response.code == "409" update end def create response = http_api.post("clients", post_data) @server_generated_private_key = response["private_key"] response end def update response = http_api.put("clients/#{name}", put_data) if response.respond_to?(:private_key) # Chef 11 @server_generated_private_key = response.private_key else # Chef 10 @server_generated_private_key = response["private_key"] end response end def put_data base_put_data = { :name => name, :admin => false } if self_generate_keys? base_put_data[:public_key] = generated_public_key else base_put_data[:private_key] = true end base_put_data end def post_data post_data = { :name => name, :admin => false } post_data[:public_key] = generated_public_key if self_generate_keys? post_data end def http_api @http_api ||= Chef::REST.new(Chef::Config[:chef_server_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]) end # Whether or not to generate keys locally and post the public key to the # server. Delegates to `Chef::Config.local_key_generation`. Servers # before 11.0 do not support this feature. def self_generate_keys? Chef::Config.local_key_generation end def private_key if self_generate_keys? generated_private_key.to_pem else @server_generated_private_key end end def generated_private_key @generated_key ||= OpenSSL::PKey::RSA.generate(2048) end def generated_public_key generated_private_key.public_key.to_pem end def file_flags base_flags = File::CREAT|File::TRUNC|File::RDWR # Windows doesn't have symlinks, so it doesn't have NOFOLLOW if defined?(File::NOFOLLOW) && !Chef::Config[:follow_client_key_symlink] base_flags |= File::NOFOLLOW end base_flags end end end end chef-12.3.0/lib/chef/scan_access_control.rb0000644000004100000410000001061712520074675020554 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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. # class Chef # == ScanAccessControl # Reads Access Control Settings on a file and writes them out to a resource # (should be the current_resource), attempting to match the style used by the # new resource, that is, if users are specified with usernames in # new_resource, then the uids from stat will be looked up and usernames will # be added to current_resource. # # === Why? # FileAccessControl objects may operate on a temporary file, in which case we # won't know if the access control settings changed (ex: rendering a template # with both a change in content and ownership). For auditing purposes, we # need to record the current state of a file system entity. #-- # Not yet sure if this is the optimal way to solve the problem. But it's # progress towards the end goal. # # TODO: figure out if all this works with OS X's negative uids # TODO: windows class ScanAccessControl attr_reader :new_resource attr_reader :current_resource def initialize(new_resource, current_resource) @new_resource, @current_resource = new_resource, current_resource end # Modifies @current_resource, setting the current access control state. def set_all! if ::File.exist?(new_resource.path) set_owner set_group set_mode else # leave the values as nil. end end # Set the owner attribute of +current_resource+ to whatever the current # state is. Attempts to match the format given in new_resource: if the # new_resource specifies the owner as a string, the username for the uid # will be looked up and owner will be set to the username, and vice versa. def set_owner @current_resource.owner(current_owner) end def current_owner case new_resource.owner when String, nil lookup_uid when Integer stat.uid else Chef::Log.error("The `owner` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.owner.inspect})") raise ArgumentError, "cannot resolve #{new_resource.owner.inspect} to uid, owner must be a string or integer" end end def lookup_uid unless (pwent = Etc.getpwuid(stat.uid)).nil? pwent.name else stat.uid end rescue ArgumentError stat.uid end # Set the group attribute of +current_resource+ to whatever the current state is. def set_group @current_resource.group(current_group) end def current_group case new_resource.group when String, nil lookup_gid when Integer stat.gid else Chef::Log.error("The `group` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.owner.inspect})") raise ArgumentError, "cannot resolve #{new_resource.group.inspect} to gid, group must be a string or integer" end end def lookup_gid unless (pwent = Etc.getgrgid(stat.gid)).nil? pwent.name else stat.gid end rescue ArgumentError stat.gid end def set_mode @current_resource.mode(current_mode) end def current_mode case new_resource.mode when String, Integer, nil "0#{(stat.mode & 07777).to_s(8)}" else Chef::Log.error("The `mode` parameter of the #@new_resource resource is set to an invalid value (#{new_resource.mode.inspect})") raise ArgumentError, "Invalid value #{new_resource.mode.inspect} for `mode` on resource #@new_resource" end end def stat @stat ||= if @new_resource.instance_of?(Chef::Resource::Link) ::File.lstat(@new_resource.path) else realpath = ::File.realpath(@new_resource.path) ::File.stat(realpath) end end end end chef-12.3.0/lib/chef/run_list/0000755000004100000410000000000012520074675016054 5ustar www-datawww-datachef-12.3.0/lib/chef/run_list/run_list_expansion.rb0000644000004100000410000001351512520074675022331 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2010, 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 'chef/mash' require 'chef/mixin/deep_merge' require 'chef/role' require 'chef/rest' class Chef class RunList # Abstract Base class for expanding a run list. Subclasses must handle # fetching roles from a data source by defining +fetch_role+ class RunListExpansion attr_reader :run_list_items # A VersionedRecipeList of recipes. Populated only after #expand # is called. attr_reader :recipes attr_reader :default_attrs attr_reader :override_attrs attr_reader :environment attr_reader :missing_roles_with_including_role # The data source passed to the constructor. Not used in this class. # In subclasses, this is a couchdb or Chef::REST object pre-configured # to fetch roles from their correct location. attr_reader :source # Returns a Hash of the form "including_role" => "included_role_or_recipe". # This can be used to show the expanded run list (ordered) graph. # ==== Caveats # * Duplicate roles are not shown. attr_reader :run_list_trace def initialize(environment, run_list_items, source=nil) @environment = environment @missing_roles_with_including_role = Array.new @run_list_items = run_list_items.dup @source = source @default_attrs = Mash.new @override_attrs = Mash.new @recipes = Chef::RunList::VersionedRecipeList.new @applied_roles = {} @run_list_trace = Hash.new {|h, key| h[key] = [] } end # Did we find any errors (expanding roles)? def errors? @missing_roles_with_including_role.length > 0 end alias :invalid? :errors? # Recurses over the run list items, expanding roles. After this, # +recipes+ will contain the fully expanded recipe list def expand # Sure do miss function arity when being recursive expand_run_list_items(@run_list_items) end # Fetches and inflates a role # === Returns # Chef::Role in most cases # false if the role has already been applied # nil if the role does not exist def inflate_role(role_name, included_by) return false if applied_role?(role_name) # Prevent infinite loops applied_role(role_name) fetch_role(role_name, included_by) end def apply_role_attributes(role) @default_attrs = Chef::Mixin::DeepMerge.merge(@default_attrs, role.default_attributes) @override_attrs = Chef::Mixin::DeepMerge.merge(@override_attrs, role.override_attributes) end def applied_role?(role_name) @applied_roles.has_key?(role_name) end # Returns an array of role names that were expanded; this # includes any roles that were in the original, pre-expansion # run_list as well as roles processed during # expansion. Populated only after #expand is called. def roles @applied_roles.keys end # In subclasses, this method will fetch the role from the data source. def fetch_role(name, included_by) raise NotImplementedError end # When a role is not found, an error message is logged, but no # exception is raised. We do add an entry in the errors collection. # === Returns # nil def role_not_found(name, included_by) Chef::Log.error("Role #{name} (included by '#{included_by}') is in the runlist but does not exist. Skipping expand.") @missing_roles_with_including_role << [name, included_by] nil end def errors @missing_roles_with_including_role.map {|item| item.first } end private # these methods modifies internal state based on arguments, so hide it. def applied_role(role_name) @applied_roles[role_name] = true end def expand_run_list_items(items, included_by="top level") if entry = items.shift @run_list_trace[included_by.to_s] << entry.to_s case entry.type when :recipe recipes.add_recipe(entry.name, entry.version) when :role if role = inflate_role(entry.name, included_by) expand_run_list_items(role.run_list_for(@environment).run_list_items, role) apply_role_attributes(role) end end expand_run_list_items(items, included_by) end end end # Expand a run list from disk. Suitable for chef-solo class RunListExpansionFromDisk < RunListExpansion def fetch_role(name, included_by) Chef::Role.from_disk(name) rescue Chef::Exceptions::RoleNotFound role_not_found(name, included_by) end end # Expand a run list from the chef-server API. class RunListExpansionFromAPI < RunListExpansion def rest @rest ||= (source || Chef::REST.new(Chef::Config[:chef_server_url])) end def fetch_role(name, included_by) rest.get_rest("roles/#{name}") rescue Net::HTTPServerException => e if e.message == '404 "Not Found"' role_not_found(name, included_by) else raise end end end end end chef-12.3.0/lib/chef/run_list/run_list_item.rb0000644000004100000410000000643112520074675021262 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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. class Chef class RunList class RunListItem QUALIFIED_RECIPE = %r{^recipe\[([^\]@]+)(@([0-9]+(\.[0-9]+){1,2}))?\]$} QUALIFIED_ROLE = %r{^role\[([^\]]+)\]$} VERSIONED_UNQUALIFIED_RECIPE = %r{^([^@]+)(@([0-9]+(\.[0-9]+){1,2}))$} FALSE_FRIEND = %r{[\[\]]} attr_reader :name, :type, :version def initialize(item) @version = nil case item when Hash assert_hash_is_valid_run_list_item!(item) @type = (item['type'] || item[:type]).to_sym @name = item['name'] || item[:name] if (item.has_key?('version') || item.has_key?(:version)) @version = item['version'] || item[:version] end when String if match = QUALIFIED_RECIPE.match(item) # recipe[recipe_name] # recipe[recipe_name@1.0.0] @type = :recipe @name = match[1] @version = match[3] if match[3] elsif match = QUALIFIED_ROLE.match(item) # role[role_name] @type = :role @name = match[1] elsif match = VERSIONED_UNQUALIFIED_RECIPE.match(item) # recipe_name@1.0.0 @type = :recipe @name = match[1] @version = match[3] if match[3] elsif match = FALSE_FRIEND.match(item) # Recipe[recipe_name] # roles[role_name] name = match[1] raise ArgumentError, "Unable to create #{self.class} from #{item.class}:#{item.inspect}: must be recipe[#{name}] or role[#{name}]" else # recipe_name @type = :recipe @name = item end else raise ArgumentError, "Unable to create #{self.class} from #{item.class}:#{item.inspect}: must be a Hash or String" end end def to_s "#{@type}[#{@name}#{@version ? "@#{@version}" :""}]" end def role? @type == :role end def recipe? @type == :recipe end def ==(other) if other.kind_of?(String) self.to_s == other.to_s else other.respond_to?(:type) && other.respond_to?(:name) && other.respond_to?(:version) && other.type == @type && other.name == @name && other.version == @version end end def assert_hash_is_valid_run_list_item!(item) unless (item.key?('type')|| item.key?(:type)) && (item.key?('name') || item.key?(:name)) raise ArgumentError, "Initializing a #{self.class} from a hash requires that it have a 'type' and 'name' key" end end end end end chef-12.3.0/lib/chef/run_list/versioned_recipe_list.rb0000644000004100000410000000435012520074675022763 0ustar www-datawww-data# # Author:: Stephen Delano () # Author:: Seth Falcon () # Copyright:: Copyright 2010 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/version_class' require 'chef/version_constraint' # Why does this class exist? # Why did we not just modify RunList/RunListItem? class Chef class RunList class VersionedRecipeList < Array def initialize super @versions = Hash.new end def add_recipe(name, version=nil) if version && @versions.has_key?(name) unless Chef::Version.new(@versions[name]) == Chef::Version.new(version) raise Chef::Exceptions::CookbookVersionConflict, "Run list requires #{name} at versions #{@versions[name]} and #{version}" end end @versions[name] = version if version self << name unless self.include?(name) end def with_versions self.map {|recipe_name| {:name => recipe_name, :version => @versions[recipe_name]}} end # Return an Array of Hashes, each of the form: # {:name => RECIPE_NAME, :version_constraint => Chef::VersionConstraint } def with_version_constraints self.map do |recipe_name| constraint = Chef::VersionConstraint.new(@versions[recipe_name]) { :name => recipe_name, :version_constraint => constraint } end end # Return an Array of Strings, each of the form: # "NAME@VERSION" def with_version_constraints_strings self.map do |recipe_name| if @versions[recipe_name] "#{recipe_name}@#{@versions[recipe_name]}" else recipe_name end end end end end end chef-12.3.0/lib/chef/version_constraint/0000755000004100000410000000000012520074675020146 5ustar www-datawww-datachef-12.3.0/lib/chef/version_constraint/platform.rb0000644000004100000410000000160112520074675022315 0ustar www-datawww-data# Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/version_constraint' require 'chef/version/platform' class Chef class VersionConstraint class Platform < Chef::VersionConstraint VERSION_CLASS = Chef::Version::Platform end end end chef-12.3.0/lib/chef/event_loggers/0000755000004100000410000000000012520074675017060 5ustar www-datawww-datachef-12.3.0/lib/chef/event_loggers/windows_eventlog.rb0000644000004100000410000000624512520074675023011 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/event_loggers/base' require 'chef/platform/query_helpers' if Chef::Platform::windows? and not Chef::Platform::windows_server_2003? if defined? Windows::Constants [:INFINITE, :WAIT_FAILED, :FORMAT_MESSAGE_IGNORE_INSERTS, :ERROR_INSUFFICIENT_BUFFER].each do |c| # These are redefined in 'win32/eventlog' Windows::Constants.send(:remove_const, c) if Windows::Constants.const_defined? c end end require 'win32/eventlog' end class Chef module EventLoggers class WindowsEventLogger < EventLoggers::Base short_name(:win_evt) # These must match those that are defined in the manifest file RUN_START_EVENT_ID = 10000 RUN_STARTED_EVENT_ID = 10001 RUN_COMPLETED_EVENT_ID = 10002 RUN_FAILED_EVENT_ID = 10003 EVENT_CATEGORY_ID = 11000 LOG_CATEGORY_ID = 11001 # Since we must install the event logger, this is not really configurable SOURCE = 'Chef' def self.available? return Chef::Platform::windows? end def initialize @eventlog = ::Win32::EventLog::open('Application') end def run_start(version) @eventlog.report_event( :event_type => ::Win32::EventLog::INFO_TYPE, :source => SOURCE, :event_id => RUN_START_EVENT_ID, :data => [version] ) end def run_started(run_status) @run_status = run_status @eventlog.report_event( :event_type => ::Win32::EventLog::INFO_TYPE, :source => SOURCE, :event_id => RUN_STARTED_EVENT_ID, :data => [run_status.run_id] ) end def run_completed(node) @eventlog.report_event( :event_type => ::Win32::EventLog::INFO_TYPE, :source => SOURCE, :event_id => RUN_COMPLETED_EVENT_ID, :data => [@run_status.run_id, @run_status.elapsed_time.to_s] ) end #Failed chef-client run %1 in %2 seconds. #Exception type: %3 #Exception message: %4 #Exception backtrace: %5 def run_failed(e) data = if @run_status [@run_status.run_id, @run_status.elapsed_time.to_s] else ["UNKNOWN", "UNKNOWN"] end @eventlog.report_event( :event_type => ::Win32::EventLog::ERROR_TYPE, :source => SOURCE, :event_id => RUN_FAILED_EVENT_ID, :data => data + [e.class.name, e.message, e.backtrace.join("\n")] ) end end end end chef-12.3.0/lib/chef/event_loggers/base.rb0000644000004100000410000000335512520074675020325 0ustar www-datawww-data# # Author:: Jay Mundrawala () # # Copyright:: 2014, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'chef/event_dispatch/base' class Chef module EventLoggers class UnknownEventLogger < StandardError; end class UnavailableEventLogger < StandardError; end def self.event_loggers_by_name @event_loggers_by_name ||= {} end def self.register(name, logger) event_loggers_by_name[name.to_s] = logger end def self.by_name(name) event_loggers_by_name[name] end def self.available_event_loggers event_loggers_by_name.select do |key, val| val.available? end.keys end def self.new(name) event_logger_class = by_name(name.to_s) or raise UnknownEventLogger, "No event logger found for #{name} (available: #{available_event_loggers.join(', ')})" raise UnavailableEventLogger unless available_event_loggers.include? name.to_s event_logger_class.new end class Base < EventDispatch::Base def self.short_name(name) Chef::EventLoggers.register(name, self) end # Returns true if this implementation of EventLoggers can be used def self.available? false end end end end chef-12.3.0/lib/chef/node.rb0000644000004100000410000004175212520074675015500 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Christopher Brown () # Author:: Christopher Walters () # Author:: Tim Hinderliter () # Copyright:: Copyright (c) 2008-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 'forwardable' require 'chef/config' require 'chef/nil_argument' require 'chef/mixin/params_validate' require 'chef/mixin/from_file' require 'chef/mixin/deep_merge' require 'chef/dsl/include_attribute' require 'chef/dsl/platform_introspection' require 'chef/environment' require 'chef/rest' require 'chef/run_list' require 'chef/node/attribute' require 'chef/mash' require 'chef/json_compat' require 'chef/search/query' require 'chef/whitelist' class Chef class Node extend Forwardable def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key? def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override! attr_accessor :recipe_list, :run_state, :override_runlist attr_accessor :chef_server_rest # RunContext will set itself as run_context via this setter when # initialized. This is needed so DSL::IncludeAttribute (in particular, # #include_recipe) can access the run_context to determine if an attributes # file has been seen yet. #-- # TODO: This is a pretty ugly way to solve that problem. attr_accessor :run_context include Chef::Mixin::FromFile include Chef::DSL::IncludeAttribute include Chef::DSL::PlatformIntrospection include Chef::Mixin::ParamsValidate # Create a new Chef::Node object. def initialize(chef_server_rest: nil) @chef_server_rest = chef_server_rest @name = nil @chef_environment = '_default' @primary_runlist = Chef::RunList.new @override_runlist = Chef::RunList.new @attributes = Chef::Node::Attribute.new({}, {}, {}, {}) @run_state = {} end # Used by DSL def node self end def chef_server_rest @chef_server_rest ||= Chef::REST.new(Chef::Config[:chef_server_url]) end # Set the name of this Node, or return the current name. def name(arg=nil) if arg != nil validate( {:name => arg }, {:name => { :kind_of => String, :cannot_be => :blank, :regex => /^[\-[:alnum:]_:.]+$/} }) @name = arg else @name end end def chef_environment(arg=nil) set_or_return( :chef_environment, arg, { :regex => /^[\-[:alnum:]_]+$/, :kind_of => String } ) end def chef_environment=(environment) chef_environment(environment) end alias :environment :chef_environment def attributes @attributes end alias :attribute :attributes alias :construct_attributes :attributes # Return an attribute of this node. Returns nil if the attribute is not found. def [](attrib) attributes[attrib] end # Set a normal attribute of this node, but auto-vivify any Mashes that # might be missing def normal attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = false attributes.normal end alias_method :set, :normal # Set a normal attribute of this node, auto-vivifying any mashes that are # missing, but if the final value already exists, don't set it def normal_unless attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = true attributes.normal end alias_method :set_unless, :normal_unless # Set a default of this node, but auto-vivify any Mashes that might # be missing def default attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = false attributes.default end # Set a default attribute of this node, auto-vivifying any mashes that are # missing, but if the final value already exists, don't set it def default_unless attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = true attributes.default end # Set an override attribute of this node, but auto-vivify any Mashes that # might be missing def override attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = false attributes.override end # Set an override attribute of this node, auto-vivifying any mashes that # are missing, but if the final value already exists, don't set it def override_unless attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = true attributes.override end alias :override_attrs :override alias :default_attrs :default alias :normal_attrs :normal def override_attrs=(new_values) attributes.override = new_values end def default_attrs=(new_values) attributes.default = new_values end def normal_attrs=(new_values) attributes.normal = new_values end def automatic_attrs attributes.top_level_breadcrumb = nil attributes.set_unless_value_present = false attributes.automatic end def automatic_attrs=(new_values) attributes.automatic = new_values end # Return true if this Node has a given attribute, false if not. Takes either a symbol or # a string. # # Only works on the top level. Preferred way is to use the normal [] style # lookup and call attribute?() def attribute?(attrib) attributes.attribute?(attrib) end # Yield each key of the top level to the block. def each(&block) attributes.each(&block) end # Iterates over each attribute, passing the attribute and value to the block. def each_attribute(&block) attributes.each_attribute(&block) end # Only works for attribute fetches, setting is no longer supported def method_missing(symbol, *args) attributes.send(symbol, *args) end # Returns true if this Node expects a given recipe, false if not. # # First, the run list is consulted to see whether the recipe is # explicitly included. If it's not there, it looks in # `node[:recipes]`, which is populated when the run_list is expanded # # NOTE: It's used by cookbook authors def recipe?(recipe_name) run_list.include?(recipe_name) || Array(self[:recipes]).include?(recipe_name) end # used by include_recipe to add recipes to the expanded run_list to be # saved back to the node and be searchable def loaded_recipe(cookbook, recipe) fully_qualified_recipe = "#{cookbook}::#{recipe}" automatic_attrs[:recipes] << fully_qualified_recipe unless Array(self[:recipes]).include?(fully_qualified_recipe) end # Returns true if this Node expects a given role, false if not. def role?(role_name) run_list.include?("role[#{role_name}]") end def primary_runlist @primary_runlist end def override_runlist(*args) args.length > 0 ? @override_runlist.reset!(args) : @override_runlist end def select_run_list @override_runlist.empty? ? @primary_runlist : @override_runlist end # Returns an Array of roles and recipes, in the order they will be applied. # If you call it with arguments, they will become the new list of roles and recipes. def run_list(*args) rl = select_run_list args.length > 0 ? rl.reset!(args) : rl end def run_list=(list) rl = select_run_list rl = list end # Returns true if this Node expects a given role, false if not. def run_list?(item) run_list.detect { |r| r == item } ? true : false end # Consume data from ohai and Attributes provided as JSON on the command line. def consume_external_attrs(ohai_data, json_cli_attrs) Chef::Log.debug("Extracting run list from JSON attributes provided on command line") consume_attributes(json_cli_attrs) self.automatic_attrs = ohai_data platform, version = Chef::Platform.find_platform_and_version(self) Chef::Log.debug("Platform is #{platform} version #{version}") self.automatic[:platform] = platform self.automatic[:platform_version] = version end # Consumes the combined run_list and other attributes in +attrs+ def consume_attributes(attrs) normal_attrs_to_merge = consume_run_list(attrs) Chef::Log.debug("Applying attributes from json file") self.normal_attrs = Chef::Mixin::DeepMerge.merge(normal_attrs,normal_attrs_to_merge) self.tags # make sure they're defined end # Lazy initializer for tags attribute def tags normal[:tags] = [] unless attribute?(:tags) normal[:tags] end def tag(*tags) tags.each do |tag| self.normal[:tags].push(tag.to_s) unless self[:tags].include? tag.to_s end self[:tags] end # Extracts the run list from +attrs+ and applies it. Returns the remaining attributes def consume_run_list(attrs) attrs = attrs ? attrs.dup : {} if new_run_list = attrs.delete("recipes") || attrs.delete("run_list") if attrs.key?("recipes") || attrs.key?("run_list") raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only." end Chef::Log.info("Setting the run_list to #{new_run_list.to_s} from CLI options") run_list(new_run_list) end attrs end # Clear defaults and overrides, so that any deleted attributes # between runs are still gone. def reset_defaults_and_overrides self.default.clear self.override.clear end # Expands the node's run list and sets the default and override # attributes. Also applies stored attributes (from json provided # on the command line) # # Returns the fully-expanded list of recipes, a RunListExpansion. # #-- # TODO: timh/cw, 5-14-2010: Should this method exist? Should we # instead modify default_attrs and override_attrs whenever our # run_list is mutated? Or perhaps do something smarter like # on-demand generation of default_attrs and override_attrs, # invalidated only when run_list is mutated? def expand!(data_source = 'server') expansion = run_list.expand(chef_environment, data_source) raise Chef::Exceptions::MissingRole, expansion if expansion.errors? self.tags # make sure they're defined automatic_attrs[:recipes] = expansion.recipes automatic_attrs[:roles] = expansion.roles apply_expansion_attributes(expansion) expansion end # Apply the default and overrides attributes from the expansion # passed in, which came from roles. def apply_expansion_attributes(expansion) loaded_environment = if chef_environment == "_default" Chef::Environment.new.tap {|e| e.name("_default")} else Chef::Environment.load(chef_environment) end attributes.env_default = loaded_environment.default_attributes attributes.env_override = loaded_environment.override_attributes attribute.role_default = expansion.default_attrs attributes.role_override = expansion.override_attrs end # Transform the node to a Hash def to_hash index_hash = Hash.new index_hash["chef_type"] = "node" index_hash["name"] = name index_hash["chef_environment"] = chef_environment attribute.each do |key, value| index_hash[key] = value end index_hash["recipe"] = run_list.recipe_names if run_list.recipe_names.length > 0 index_hash["role"] = run_list.role_names if run_list.role_names.length > 0 index_hash["run_list"] = run_list.run_list_items index_hash end def display_hash display = {} display["name"] = name display["chef_environment"] = chef_environment display["automatic"] = automatic_attrs display["normal"] = normal_attrs display["default"] = attributes.combined_default display["override"] = attributes.combined_override display["run_list"] = run_list.run_list_items display end # Serialize this object as a hash def to_json(*a) Chef::JSONCompat.to_json(for_json, *a) end def for_json result = { "name" => name, "chef_environment" => chef_environment, 'json_class' => self.class.name, "automatic" => attributes.automatic, "normal" => attributes.normal, "chef_type" => "node", "default" => attributes.combined_default, "override" => attributes.combined_override, #Render correctly for run_list items so malformed json does not result "run_list" => @primary_runlist.run_list.map { |item| item.to_s } } result end def update_from!(o) run_list.reset!(o.run_list) self.automatic_attrs = o.automatic_attrs self.normal_attrs = o.normal_attrs self.override_attrs = o.override_attrs self.default_attrs = o.default_attrs chef_environment(o.chef_environment) self end # Create a Chef::Node from JSON def self.json_create(o) node = new node.name(o["name"]) node.chef_environment(o["chef_environment"]) if o.has_key?("attributes") node.normal_attrs = o["attributes"] end node.automatic_attrs = Mash.new(o["automatic"]) if o.has_key?("automatic") node.normal_attrs = Mash.new(o["normal"]) if o.has_key?("normal") node.default_attrs = Mash.new(o["default"]) if o.has_key?("default") node.override_attrs = Mash.new(o["override"]) if o.has_key?("override") if o.has_key?("run_list") node.run_list.reset!(o["run_list"]) else o["recipes"].each { |r| node.recipes << r } end node end def self.list_by_environment(environment, inflate=false) if inflate response = Hash.new Chef::Search::Query.new.search(:node, "chef_environment:#{environment}") {|n| response[n.name] = n unless n.nil?} response else Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("environments/#{environment}/nodes") end end def self.list(inflate=false) if inflate response = Hash.new Chef::Search::Query.new.search(:node) do |n| response[n.name] = n unless n.nil? end response else Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes") end end def self.find_or_create(node_name) load(node_name) rescue Net::HTTPServerException => e raise unless e.response.code == '404' node = build(node_name) node.create end def self.build(node_name) node = new node.name(node_name) node.chef_environment(Chef::Config[:environment]) unless Chef::Config[:environment].nil? || Chef::Config[:environment].chomp.empty? node end # Load a node by name def self.load(name) Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}") end # Remove this node via the REST API def destroy chef_server_rest.delete_rest("nodes/#{name}") end # Save this node via the REST API def save # Try PUT. If the node doesn't yet exist, PUT will return 404, # so then POST to create. begin if Chef::Config[:why_run] Chef::Log.warn("In whyrun mode, so NOT performing node save.") else chef_server_rest.put_rest("nodes/#{name}", data_for_save) end rescue Net::HTTPServerException => e raise e unless e.response.code == "404" chef_server_rest.post_rest("nodes", data_for_save) end self end # Create the node via the REST API def create chef_server_rest.post_rest("nodes", data_for_save) self end def to_s "node[#{name}]" end def <=>(other_node) self.name <=> other_node.name end private def data_for_save data = for_json ["automatic", "default", "normal", "override"].each do |level| whitelist_config_option = "#{level}_attribute_whitelist".to_sym whitelist = Chef::Config[whitelist_config_option] unless whitelist.nil? # nil => save everything Chef::Log.info("Whitelisting #{level} node attributes for save.") data[level] = Chef::Whitelist.filter(data[level], whitelist) end end data end end end chef-12.3.0/lib/chef/recipe.rb0000644000004100000410000000732012520074675016013 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: Christopher Walters () # Copyright:: Copyright (c) 2008, 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/dsl/recipe' require 'chef/dsl/data_query' require 'chef/dsl/platform_introspection' require 'chef/dsl/include_recipe' require 'chef/dsl/registry_helper' require 'chef/dsl/reboot_pending' require 'chef/dsl/audit' require 'chef/dsl/powershell' require 'chef/mixin/from_file' require 'chef/mixin/deprecation' class Chef # == Chef::Recipe # A Recipe object is the context in which Chef recipes are evaluated. class Recipe include Chef::DSL::DataQuery include Chef::DSL::PlatformIntrospection include Chef::DSL::IncludeRecipe include Chef::DSL::Recipe include Chef::DSL::RegistryHelper include Chef::DSL::RebootPending include Chef::DSL::Audit include Chef::DSL::Powershell include Chef::Mixin::FromFile include Chef::Mixin::Deprecation attr_accessor :cookbook_name, :recipe_name, :recipe, :params, :run_context # Parses a potentially fully-qualified recipe name into its # cookbook name and recipe short name. # # For example: # "aws::elastic_ip" returns [:aws, "elastic_ip"] # "aws" returns [:aws, "default"] # "::elastic_ip" returns [ current_cookbook, "elastic_ip" ] #-- # TODO: Duplicates functionality of RunListItem def self.parse_recipe_name(recipe_name, current_cookbook: nil) case recipe_name when /(.+?)::(.+)/ [ $1.to_sym, $2 ] when /^::(.+)/ raise "current_cookbook is nil, cannot resolve #{recipe_name}" if current_cookbook.nil? [ current_cookbook.to_sym, $1 ] else [ recipe_name.to_sym, "default" ] end end def initialize(cookbook_name, recipe_name, run_context) @cookbook_name = cookbook_name @recipe_name = recipe_name @run_context = run_context # TODO: 5/19/2010 cw/tim: determine whether this can be removed @params = Hash.new end # Used in DSL mixins def node run_context.node end # Used by the DSL to look up resources when executing in the context of a # recipe. def resources(*args) run_context.resource_collection.find(*args) end # This was moved to Chef::Node#tag, redirecting here for compatibility def tag(*tags) run_context.node.tag(*tags) end # Returns true if the node is tagged with *all* of the supplied +tags+. # # === Parameters # tags:: A list of tags # # === Returns # true:: If all the parameters are present # false:: If any of the parameters are missing def tagged?(*tags) return false if run_context.node[:tags].nil? tags.each do |tag| return false unless run_context.node[:tags].include?(tag) end true end # Removes the list of tags from the node. # # === Parameters # tags:: A list of tags # # === Returns # tags:: The current list of run_context.node[:tags] def untag(*tags) tags.each do |tag| run_context.node.normal[:tags].delete(tag) end end end end chef-12.3.0/lib/chef/exceptions.rb0000644000004100000410000004215112520074675016726 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Seth Falcon () # Author:: Kyle Goodwin () # Copyright:: Copyright 2008-2010 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. class Chef # == Chef::Exceptions # Chef's custom exceptions are all contained within the Chef::Exceptions # namespace. class Exceptions # Backcompat with Chef::ShellOut code: require 'mixlib/shellout/exceptions' def self.const_missing(const_name) if const_name == :ShellCommandFailed Chef::Log.warn("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed") called_from = caller[0..3].inject("Called from:\n") {|msg, trace_line| msg << " #{trace_line}\n" } Chef::Log.warn(called_from) Mixlib::ShellOut::ShellCommandFailed else super end end class Application < RuntimeError; end class Cron < RuntimeError; end class Env < RuntimeError; end class Exec < RuntimeError; end class ErlCall < RuntimeError; end class FileNotFound < RuntimeError; end class Package < RuntimeError; end class Service < RuntimeError; end class Script < RuntimeError; end class Route < RuntimeError; end class SearchIndex < RuntimeError; end class Override < RuntimeError; end class UnsupportedAction < RuntimeError; end class MissingLibrary < RuntimeError; end class CannotDetermineNodeName < RuntimeError def initialize super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn" end end class User < RuntimeError; end class Group < RuntimeError; end class Link < RuntimeError; end class Mount < RuntimeError; end class PrivateKeyMissing < RuntimeError; end class CannotWritePrivateKey < RuntimeError; end class RoleNotFound < RuntimeError; end class DuplicateRole < RuntimeError; end class ValidationFailed < ArgumentError; end class InvalidPrivateKey < ArgumentError; end class ConfigurationError < ArgumentError; end class RedirectLimitExceeded < RuntimeError; end class AmbiguousRunlistSpecification < ArgumentError; end class CookbookFrozen < ArgumentError; end class CookbookNotFound < RuntimeError; end # Cookbook loader used to raise an argument error when cookbook not found. # for back compat, need to raise an error that inherits from ArgumentError class CookbookNotFoundInRepo < ArgumentError; end class RecipeNotFound < ArgumentError; end class AttributeNotFound < RuntimeError; end class MissingCookbookDependency < StandardError; end # CHEF-5120 class InvalidCommandOption < RuntimeError; end class CommandTimeout < RuntimeError; end class RequestedUIDUnavailable < RuntimeError; end class InvalidHomeDirectory < ArgumentError; end class DsclCommandFailed < RuntimeError; end class PlistUtilCommandFailed < RuntimeError; end class UserIDNotFound < ArgumentError; end class GroupIDNotFound < ArgumentError; end class ConflictingMembersInGroup < ArgumentError; end class InvalidResourceReference < RuntimeError; end class ResourceNotFound < RuntimeError; end class VerificationNotFound < RuntimeError; end # Can't find a Resource of this type that is valid on this platform. class NoSuchResourceType < NameError def initialize(short_name, node) super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]}" end end class InvalidResourceSpecification < ArgumentError; end class SolrConnectionError < RuntimeError; end class IllegalChecksumRevert < RuntimeError; end class CookbookVersionNameMismatch < ArgumentError; end class MissingParentDirectory < RuntimeError; end class UnresolvableGitReference < RuntimeError; end class InvalidRemoteGitReference < RuntimeError; end class InvalidEnvironmentRunListSpecification < ArgumentError; end class InvalidDataBagItemID < ArgumentError; end class InvalidDataBagName < ArgumentError; end class EnclosingDirectoryDoesNotExist < ArgumentError; end # Errors originating from calls to the Win32 API class Win32APIError < RuntimeError; end # Thrown when Win32 API layer binds to non-existent Win32 function. Occurs # when older versions of Windows don't support newer Win32 API functions. class Win32APIFunctionNotImplemented < NotImplementedError; end # Attempting to run windows code on a not-windows node class Win32NotWindows < RuntimeError; end class WindowsNotAdmin < RuntimeError; end # Attempting to access a 64-bit only resource on a 32-bit Windows system class Win32ArchitectureIncorrect < RuntimeError; end class ObsoleteDependencySyntax < ArgumentError; end class InvalidDataBagPath < ArgumentError; end class DuplicateDataBagItem < RuntimeError; end class PowershellCmdletException < RuntimeError; end class LCMParser < RuntimeError; end class CannotDetermineHomebrewOwner < Package; end # Can not create staging file during file deployment class FileContentStagingError < RuntimeError def initialize(errors) super "Staging tempfile can not be created during file deployment.\n Errors: #{errors.join('\n')}!" end end # A different version of a cookbook was added to a # VersionedRecipeList than the one already there. class CookbookVersionConflict < ArgumentError ; end # does not follow X.Y.Z format. ArgumentError? class InvalidPlatformVersion < ArgumentError; end class InvalidCookbookVersion < ArgumentError; end # version constraint should be a string or array, or it doesn't # match OP VERSION. ArgumentError? class InvalidVersionConstraint < ArgumentError; end # Version constraints are not allowed in chef-solo class IllegalVersionConstraint < NotImplementedError; end class MetadataNotValid < StandardError; end class MetadataNotFound < StandardError attr_reader :install_path attr_reader :cookbook_name def initialize(install_path, cookbook_name) @install_path = install_path @cookbook_name = cookbook_name super "No metadata.rb or metadata.json found for cookbook #{@cookbook_name} in #{@install_path}" end end # File operation attempted but no permissions to perform it class InsufficientPermissions < RuntimeError; end # Ifconfig failed class Ifconfig < RuntimeError; end # Invalid "source" parameter to a remote_file resource class InvalidRemoteFileURI < ArgumentError; end # Node::Attribute computes the merged version of of attributes # and makes it read-only. Attempting to modify a read-only # attribute will cause this error. class ImmutableAttributeModification < NoMethodError def initialize super "Node attributes are read-only when you do not specify which precedence level to set. " + %Q(To set an attribute use code like `node.default["key"] = "value"') end end # Merged node attributes are invalidated when the component # attributes are updated. Attempting to read from a stale copy # of merged attributes will trigger this error. class StaleAttributeRead < StandardError; end # Registry Helper throws the following errors class Win32RegArchitectureIncorrect < Win32ArchitectureIncorrect; end class Win32RegHiveMissing < ArgumentError; end class Win32RegKeyMissing < RuntimeError; end class Win32RegValueMissing < RuntimeError; end class Win32RegDataMissing < RuntimeError; end class Win32RegValueExists < RuntimeError; end class Win32RegNoRecursive < ArgumentError; end class Win32RegTypeDoesNotExist < ArgumentError; end class Win32RegBadType < ArgumentError; end class Win32RegBadValueSize < ArgumentError; end class Win32RegTypesMismatch < ArgumentError; end class InvalidEnvironmentPath < ArgumentError; end class EnvironmentNotFound < RuntimeError; end # File-like resource found a non-file (socket, pipe, directory, etc) at its destination class FileTypeMismatch < RuntimeError; end # File (or descendent) resource configured to manage symlink source, but # the symlink that is there either loops or points to a nonexistent file class InvalidSymlink < RuntimeError; end class ChildConvergeError < RuntimeError; end class NoProviderAvailable < RuntimeError; end class DeprecatedFeatureError < RuntimeError; def initalize(message) super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)") end end class MissingRole < RuntimeError NULL = Object.new attr_reader :expansion def initialize(message_or_expansion=NULL) @expansion = nil case message_or_expansion when NULL super() when String super when RunList::RunListExpansion @expansion = message_or_expansion missing_roles = @expansion.errors.join(', ') super("The expanded run list includes nonexistent roles: #{missing_roles}") end end end # Exception class for collecting multiple failures. Used when running # delayed notifications so that chef can process each delayed # notification even if chef client or other notifications fail. class MultipleFailures < StandardError def initialize(*args) super @all_failures = [] end def message base = "Multiple failures occurred:\n" @all_failures.inject(base) do |message, (location, error)| message << "* #{error.class} occurred in #{location}: #{error.message}\n" end end def client_run_failure(exception) set_backtrace(exception.backtrace) @all_failures << [ "chef run", exception ] end def notification_failure(exception) @all_failures << [ "delayed notification", exception ] end def raise! unless empty? raise self.for_raise end end def empty? @all_failures.empty? end def for_raise if @all_failures.size == 1 @all_failures[0][1] else self end end end class CookbookVersionSelection # Compound exception: In run_list expansion and resolution, # run_list items referred to cookbooks that don't exist and/or # have no versions available. class InvalidRunListItems < StandardError attr_reader :non_existent_cookbooks attr_reader :cookbooks_with_no_matching_versions def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions) super(message) @non_existent_cookbooks = non_existent_cookbooks @cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions end def to_json(*a) result = { "message" => message, "non_existent_cookbooks" => non_existent_cookbooks, "cookbooks_with_no_versions" => cookbooks_with_no_matching_versions } Chef::JSONCompat.to_json(result, *a) end end # In run_list expansion and resolution, a constraint was # unsatisfiable. # # This exception may not be the complete error report. If you # resolve the misconfiguration represented by this exception and # re-solve, you may get another exception class UnsatisfiableRunListItem < StandardError attr_reader :run_list_item attr_reader :non_existent_cookbooks, :most_constrained_cookbooks # most_constrained_cookbooks: if I were to remove constraints # regarding these cookbooks, I would get a solution or move on # to the next error (deeper in the graph). An item in this list # may be unsatisfiable, but when resolved may also reveal # further unsatisfiable constraints; this condition would not be # reported. def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks) super(message) @run_list_item = run_list_item @non_existent_cookbooks = non_existent_cookbooks @most_constrained_cookbooks = most_constrained_cookbooks end def to_json(*a) result = { "message" => message, "unsatisfiable_run_list_item" => run_list_item, "non_existent_cookbooks" => non_existent_cookbooks, "most_constrained_cookbooks" => most_constrained_cookbooks } Chef::JSONCompat.to_json(result, *a) end end end # CookbookVersionSelection # When the server sends a redirect, RFC 2616 states a user-agent should # not follow it with a method other than GET or HEAD, unless a specific # action is taken by the user. A redirect received as response to a # non-GET and non-HEAD request will thus raise an InvalidRedirect. class InvalidRedirect < StandardError; end # Raised when the content length of a download does not match the content # length declared in the http response. class ContentLengthMismatch < RuntimeError def initialize(response_length, content_length) super "Response body length #{response_length} does not match HTTP Content-Length header #{content_length}." end end class UnsupportedPlatform < RuntimeError def initialize(platform) super "This functionality is not supported on platform #{platform}." end end # Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails # to release the run lock becure Chef::Config[:run_lock_timeout] seconds pass. class RunLockTimeout < RuntimeError def initialize(duration, blocking_pid) super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release." end end class ChecksumMismatch < RuntimeError def initialize(res_cksum, cont_cksum) super "Checksum on resource (#{res_cksum}) does not match checksum on content (#{cont_cksum})" end end class BadProxyURI < RuntimeError; end # Raised by Chef::JSONCompat class JSON class EncodeError < RuntimeError; end class ParseError < RuntimeError; end end class InvalidSearchQuery < ArgumentError; end # Raised by Chef::ProviderResolver class AmbiguousProviderResolution < RuntimeError def initialize(resource, classes) super "Found more than one provider for #{resource.resource_name} resource: #{classes}" end end class AuditControlGroupDuplicate < RuntimeError def initialize(name) super "Control group with name '#{name}' has already been defined" end end class AuditNameMissing < RuntimeError; end class NoAuditsProvided < RuntimeError def initialize super "You must provide a block with controls" end end class AuditsFailed < RuntimeError def initialize(num_failed, num_total) super "Audit phase found failures - #{num_failed}/#{num_total} controls failed" end end # If a converge or audit fails, we want to wrap the output from those errors into 1 error so we can # see both issues in the output. It is possible that nil will be provided. You must call `fill_backtrace` # to correctly populate the backtrace with the wrapped backtraces. class RunFailedWrappingError < RuntimeError attr_reader :wrapped_errors def initialize(*errors) errors = errors.select {|e| !e.nil?} output = "Found #{errors.size} errors, they are stored in the backtrace" @wrapped_errors = errors super output end def fill_backtrace backtrace = [] wrapped_errors.each_with_index do |e,i| backtrace << "#{i+1}) #{e.class} - #{e.message}" backtrace += e.backtrace if e.backtrace backtrace << "" end set_backtrace(backtrace) end end class PIDFileLockfileMatch < RuntimeError def initialize super "PID file and lockfile are not permitted to match. Specify a different location with --pid or --lockfile" end end class MultipleDscResourcesFound < RuntimeError attr_reader :resources_found def initialize(resources_found) @resources_found = resources_found matches_info = @resources_found.each do |r| if r['Module'].nil? "Resource #{r['Name']} was found in #{r['Module']['Name']}" else "Resource #{r['Name']} is a binary resource" end end super "Found multiple matching resources. #{matches_info.join("\n")}" end end end end chef-12.3.0/lib/chef/event_dispatch/0000755000004100000410000000000012520074675017215 5ustar www-datawww-datachef-12.3.0/lib/chef/event_dispatch/events_output_stream.rb0000644000004100000410000000134112520074675024040 0ustar www-datawww-dataclass Chef module EventDispatch class EventsOutputStream # This is a fake stream that connects to events. # # == Arguments # events: the EventDispatch object to send data to (run_context.events) # options is a hash with these possible options: # - name: a string that identifies the stream to the user. Preferably short. def initialize(events, options = {}) @events = events @options = options events.stream_opened(self, options) end attr_reader :options attr_reader :events def print(str) events.stream_output(self, str, options) end def close events.stream_closed(self, options) end end end end chef-12.3.0/lib/chef/event_dispatch/base.rb0000644000004100000410000002626512520074675020467 0ustar www-datawww-dataclass Chef # ==EventDispatch # Classes in EventDispatch deal with collecting, distributing, and handling # information in response to events that occur during a chef-client run. # # EventDispatch uses a simple publishing system where data from all events # are forwarded to all subscribers unconditionally. # # EventDispatch is used to implement custom console output formatters so that # users may have more control over the formatting and verbosity of Chef # client output and client-side data collection for server-side client # history storage and reporting. # # === API Stability Status # The EventDispatch API is intended to become a stable, public API upon which # end-users can implement their own custom output formatters, reporting # integration libraries, and more. This is a new feature, however, so # breaking changes may be required as it "bakes" in order to provide a clean, # coherent and supportable API in the long term. Therefore, developers should # consider the feature "beta" for now and be prepared for possible breaking # changes in point releases. module EventDispatch # == EventDispatch::Base # EventDispatch::Base is a completely abstract base class that defines the # API used by both the classes that collect event information and those # that process them. class Base # Called at the very start of a Chef Run def run_start(version) end def run_started(run_status) end # Called at the end a successful Chef run. def run_completed(node) end # Called at the end of a failed Chef run. def run_failed(exception) end # Called right after ohai runs. def ohai_completed(node) end # Already have a client key, assuming this node has registered. def skipping_registration(node_name, config) end # About to attempt to register as +node_name+ def registration_start(node_name, config) end def registration_completed end # Failed to register this client with the server. def registration_failed(node_name, exception, config) end # Called before Chef client loads the node data from the server def node_load_start(node_name, config) end # TODO: def node_run_list_overridden(*args) # Failed to load node data from the server def node_load_failed(node_name, exception, config) end # Error expanding the run list def run_list_expand_failed(node, exception) end # Called after Chef client has loaded the node data. # Default and override attrs from roles have been computed, but not yet applied. # Normal attrs from JSON have been added to the node. def node_load_completed(node, expanded_run_list, config) end # Called before the cookbook collection is fetched from the server. def cookbook_resolution_start(expanded_run_list) end # Called when there is an error getting the cookbook collection from the # server. def cookbook_resolution_failed(expanded_run_list, exception) end # Called when the cookbook collection is returned from the server. def cookbook_resolution_complete(cookbook_collection) end # Called before unneeded cookbooks are removed def cookbook_clean_start end # Called after the file at +path+ is removed. It may be removed if the # cookbook containing it was removed from the run list, or if the file was # removed from the cookbook. def removed_cookbook_file(path) end # Called when cookbook cleaning is finished. def cookbook_clean_complete end # Called before cookbook sync starts def cookbook_sync_start(cookbook_count) end # Called when cookbook +cookbook_name+ has been sync'd def synchronized_cookbook(cookbook_name) end # Called when an individual file in a cookbook has been updated def updated_cookbook_file(cookbook_name, path) end # Called when an error occurs during cookbook sync def cookbook_sync_failed(cookbooks, exception) end # Called after all cookbooks have been sync'd. def cookbook_sync_complete end ## TODO: add cookbook name to the API for file load callbacks ## TODO: add callbacks for overall cookbook eval start and complete. # Called when library file loading starts def library_load_start(file_count) end # Called when library file has been loaded def library_file_loaded(path) end # Called when a library file has an error on load. def library_file_load_failed(path, exception) end # Called when library file loading has finished def library_load_complete end # Called when LWRP loading starts def lwrp_load_start(lwrp_file_count) end # Called after a LWR or LWP has been loaded def lwrp_file_loaded(path) end # Called after a LWR or LWP file errors on load def lwrp_file_load_failed(path, exception) end # Called when LWRPs are finished loading def lwrp_load_complete end # Called before attribute files are loaded def attribute_load_start(attribute_file_count) end # Called after the attribute file is loaded def attribute_file_loaded(path) end # Called when an attribute file fails to load. def attribute_file_load_failed(path, exception) end # Called when attribute file loading is finished def attribute_load_complete end # Called before resource definitions are loaded def definition_load_start(definition_file_count) end # Called when a resource definition has been loaded def definition_file_loaded(path) end # Called when a resource definition file fails to load def definition_file_load_failed(path, exception) end # Called when resource definitions are done loading def definition_load_complete end # Called before recipes are loaded def recipe_load_start(recipe_count) end # Called after the recipe has been loaded def recipe_file_loaded(path) end # Called after a recipe file fails to load def recipe_file_load_failed(path, exception) end # Called when a recipe cannot be resolved def recipe_not_found(exception) end # Called when recipes have been loaded. def recipe_load_complete end # Called before convergence starts def converge_start(run_context) end # Called when the converge phase is finished. def converge_complete end # Called if the converge phase fails def converge_failed(exception) end ################################## # Audit Mode Events # This phase is currently experimental and these event APIs are subject to change ################################## # Called before audit phase starts def audit_phase_start(run_status) end # Called when audit phase successfully finishes def audit_phase_complete end # Called if there is an uncaught exception during the audit phase. The audit runner should # be catching and handling errors from the examples, so this is only uncaught errors (like # bugs in our handling code) def audit_phase_failed(exception) end # Signifies the start of a `control_group` block with a defined name def control_group_started(name) end # An example in a `control_group` block completed successfully def control_example_success(control_group_name, example_data) end # An example in a `control_group` block failed with the provided error def control_example_failure(control_group_name, example_data, error) end # TODO: need events for notification resolve? # def notifications_resolved # end # Called before action is executed on a resource. def resource_action_start(resource, action, notification_type=nil, notifier=nil) end # Called when a resource fails, but will retry. def resource_failed_retriable(resource, action, retry_count, exception) end # Called when a resource fails and will not be retried. def resource_failed(resource, action, exception) end # Called when a resource action has been skipped b/c of a conditional def resource_skipped(resource, action, conditional) end # Called when a resource action has been completed def resource_completed(resource) end # Called after #load_current_resource has run. def resource_current_state_loaded(resource, action, current_resource) end # Called when resource current state load is skipped due to the provider # not supporting whyrun mode. def resource_current_state_load_bypassed(resource, action, current_resource) end # Called when evaluating a resource that does not support whyrun in whyrun mode def resource_bypassed(resource, action, current_resource) end # Called when a resource has no converge actions, e.g., it was already correct. def resource_up_to_date(resource, action) end # Called when a change has been made to a resource. May be called multiple # times per resource, e.g., a file may have its content updated, and then # its permissions updated. def resource_update_applied(resource, action, update) end # Called after a resource has been completely converged, but only if # modifications were made. def resource_updated(resource, action) end # A stream has opened. def stream_opened(stream, options = {}) end # A stream has closed. def stream_closed(stream, options = {}) end # A chunk of data from a stream. The stream is managed by "stream," which # can be any tag whatsoever. Data in different "streams" may not be placed # on the same line or even sent to the same console. def stream_output(stream, output, options = {}) end # Called before handlers run def handlers_start(handler_count) end # Called after an individual handler has run def handler_executed(handler) end # Called after all handlers have executed def handlers_completed end # Called when an assertion declared by a provider fails def provider_requirement_failed(action, resource, exception, message) end # Called when a provider makes an assumption after a failed assertion # in whyrun mode, in order to allow execution to continue def whyrun_assumption(action, resource, message) end ## TODO: deprecation warning. this way we can queue them up and present # them all at once. # An uncategorized message. This supports the case that a user needs to # pass output that doesn't fit into one of the callbacks above. Note that # there's no semantic information about the content or importance of the # message. That means that if you're using this too often, you should add a # callback for it. def msg(message) end end end end chef-12.3.0/lib/chef/event_dispatch/dispatcher.rb0000644000004100000410000000205312520074675021670 0ustar www-datawww-datarequire 'chef/event_dispatch/base' class Chef module EventDispatch # == EventDispatch::Dispatcher # The Dispatcher handles receiving event data from the sources # (Chef::Client, Resources and Providers, etc.) and publishing the data to # the registered subscribers. class Dispatcher < Base def initialize(*subscribers) @subscribers = subscribers end # Add a new subscriber to the list of registered subscribers def register(subscriber) @subscribers << subscriber end #### # All messages are unconditionally forwarded to all subscribers, so just # define the forwarding in one go: # # Define a method that will be forwarded to all def self.def_forwarding_method(method_name) define_method(method_name) do |*args| @subscribers.each { |s| s.send(method_name, *args) } end end (Base.instance_methods - Object.instance_methods).each do |method_name| def_forwarding_method(method_name) end end end end chef-12.3.0/lib/chef/run_status.rb0000644000004100000410000000625112520074675016755 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2010 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. # # == Chef::RunStatus # Tracks various aspects of a Chef run, including the Node and RunContext, # start and end time, and any Exception that stops the run. RunStatus objects # are passed to any notification or exception handlers at the completion of a # Chef run. class Chef::RunStatus attr_reader :events attr_reader :run_context attr_writer :run_context attr_reader :start_time attr_reader :end_time attr_reader :exception attr_writer :exception attr_accessor :run_id def initialize(node, events) @node = node @events = events end def node @node end # sets +start_time+ to the current time. def start_clock @start_time = Time.now end # sets +end_time+ to the current time def stop_clock @end_time = Time.now end # The elapsed time between +start_time+ and +end_time+. Returns +nil+ if # either value is not set. def elapsed_time if @start_time && @end_time @end_time - @start_time else nil end end # The list of all resources in the current run context's +resource_collection+ def all_resources @run_context && @run_context.resource_collection.all_resources end # The list of all resources in the current run context's +resource_collection+ # that are marked as updated def updated_resources @run_context && @run_context.resource_collection.select { |r| r.updated } end # The backtrace from +exception+, if any def backtrace @exception && @exception.backtrace end # Did the Chef run fail? def failed? !success? end # Did the chef run succeed? returns +true+ if no exception has been set. def success? @exception.nil? end # A Hash representation of the RunStatus, with the following (Symbol) keys: # * :node # * :success # * :start_time # * :end_time # * :elapsed_time # * :all_resources # * :updated_resources # * :exception # * :backtrace def to_hash # use a flat hash here so we can't errors from intermediate values being nil { :node => node, :success => success?, :start_time => start_time, :end_time => end_time, :elapsed_time => elapsed_time, :all_resources => all_resources, :updated_resources => updated_resources, :exception => formatted_exception, :backtrace => backtrace, :run_id => run_id} end # Returns a string of the format "ExceptionClass: message" or +nil+ if no # +exception+ is set. def formatted_exception @exception && "#{@exception.class.name}: #{@exception.message}" end end chef-12.3.0/lib/chef/version/0000755000004100000410000000000012520074675015702 5ustar www-datawww-datachef-12.3.0/lib/chef/version/platform.rb0000644000004100000410000000257012520074675020057 0ustar www-datawww-data# Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2013 Onddo Labs, SL. # 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/version_class' class Chef class Version class Platform < Chef::Version protected def parse(str="") @major, @minor, @patch = case str.to_s when /^(\d+)\.(\d+)\.(\d+)$/ [ $1.to_i, $2.to_i, $3.to_i ] when /^(\d+)\.(\d+)$/ [ $1.to_i, $2.to_i, 0 ] when /^(\d+)$/ [ $1.to_i, 0, 0 ] when /^(\d+).(\d+)-[a-z]+\d?(-p(\d+))?$/i # Match FreeBSD [ $1.to_i, $2.to_i, ($4 ? $4.to_i : 0)] else msg = "'#{str.to_s}' does not match 'x.y.z', 'x.y' or 'x'" raise Chef::Exceptions::InvalidPlatformVersion.new( msg ) end end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/0000755000004100000410000000000012520074675021032 5ustar www-datawww-datachef-12.3.0/lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb0000644000004100000410000000142512520074675032254 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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. # class Chef::EncryptedDataBagItem class UnacceptableEncryptedDataBagItemFormat < StandardError end end chef-12.3.0/lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb0000644000004100000410000000142412520074675032215 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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. # class Chef::EncryptedDataBagItem class UnsupportedEncryptedDataBagItemFormat < StandardError end end chef-12.3.0/lib/chef/encrypted_data_bag_item/encryptor.rb0000644000004100000410000001606712520074675023416 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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 'base64' require 'digest/sha2' require 'openssl' require 'ffi_yajl' require 'chef/encrypted_data_bag_item' require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format' require 'chef/encrypted_data_bag_item/encryption_failure' require 'chef/encrypted_data_bag_item/assertions' class Chef::EncryptedDataBagItem # Implementation class for converting plaintext data bag item values to an # encrypted value, including any necessary wrappers and metadata. module Encryptor # "factory" method that creates an encryptor object with the proper class # for the desired encrypted data bag format version. # # +Chef::Config[:data_bag_encrypt_version]+ determines which version is used. def self.new(value, secret, iv=nil) format_version = Chef::Config[:data_bag_encrypt_version] case format_version when 1 Version1Encryptor.new(value, secret, iv) when 2 Version2Encryptor.new(value, secret, iv) when 3 Version3Encryptor.new(value, secret, iv) else raise UnsupportedEncryptedDataBagItemFormat, "Invalid encrypted data bag format version `#{format_version}'. Supported versions are '1', '2', '3'" end end class Version1Encryptor attr_reader :key attr_reader :plaintext_data include Chef::EncryptedDataBagItem::Assertions # Create a new Encryptor for +data+, which will be encrypted with the given # +key+. # # === Arguments: # * data: An object of any type that can be serialized to json # * key: A String representing the desired passphrase # * iv: The optional +iv+ parameter is intended for testing use only. When # *not* supplied, Encryptor will use OpenSSL to generate a secure random # IV, which is what you want. def initialize(plaintext_data, key, iv=nil) @plaintext_data = plaintext_data @key = key @iv = iv && Base64.decode64(iv) end # Returns the used encryption algorithm def algorithm ALGORITHM end # Returns a wrapped and encrypted version of +plaintext_data+ suitable for # using as the value in an encrypted data bag item. def for_encrypted_item { "encrypted_data" => encrypted_data, "iv" => Base64.encode64(iv), "version" => 1, "cipher" => algorithm } end # Generates or returns the IV. def iv # Generated IV comes from OpenSSL::Cipher#random_iv # This gets generated when +openssl_encryptor+ gets created. openssl_encryptor if @iv.nil? @iv end # Generates (and memoizes) an OpenSSL::Cipher object and configures # it for the specified iv and encryption key. def openssl_encryptor @openssl_encryptor ||= begin encryptor = OpenSSL::Cipher.new(algorithm) encryptor.encrypt # We must set key before iv: https://bugs.ruby-lang.org/issues/8221 encryptor.key = OpenSSL::Digest::SHA256.digest(key) @iv ||= encryptor.random_iv encryptor.iv = @iv encryptor end end # Encrypts and Base64 encodes +serialized_data+ def encrypted_data @encrypted_data ||= begin enc_data = openssl_encryptor.update(serialized_data) enc_data << openssl_encryptor.final Base64.encode64(enc_data) end end # Wraps the data in a single key Hash (JSON Object) and converts to JSON. # The wrapper is required because we accept values (such as Integers or # Strings) that do not produce valid JSON when serialized without the # wrapper. def serialized_data FFI_Yajl::Encoder.encode(:json_wrapper => plaintext_data) end def self.encryptor_keys %w( encrypted_data iv version cipher ) end end class Version2Encryptor < Version1Encryptor # Returns a wrapped and encrypted version of +plaintext_data+ suitable for # using as the value in an encrypted data bag item. def for_encrypted_item { "encrypted_data" => encrypted_data, "hmac" => hmac, "iv" => Base64.encode64(iv), "version" => 2, "cipher" => algorithm } end # Generates an HMAC-SHA2-256 of the encrypted data (encrypt-then-mac) def hmac @hmac ||= begin digest = OpenSSL::Digest.new("sha256") raw_hmac = OpenSSL::HMAC.digest(digest, key, encrypted_data) Base64.encode64(raw_hmac) end end def self.encryptor_keys super + %w( hmac ) end end class Version3Encryptor < Version1Encryptor include Chef::EncryptedDataBagItem::Assertions def initialize(plaintext_data, key, iv=nil) super assert_aead_requirements_met!(algorithm) @auth_tag = nil end # Returns a wrapped and encrypted version of +plaintext_data+ suitable for # using as the value in an encrypted data bag item. def for_encrypted_item { "encrypted_data" => encrypted_data, "iv" => Base64.encode64(iv), "auth_tag" => Base64.encode64(auth_tag), "version" => 3, "cipher" => algorithm } end # Returns the used encryption algorithm def algorithm AEAD_ALGORITHM end # Returns a wrapped and encrypted version of +plaintext_data+ suitable for # Returns the auth_tag. def auth_tag # Generated auth_tag comes from OpenSSL::Cipher#auth_tag # This must be generated after the data is encrypted if @auth_tag.nil? raise EncryptionFailure, "Internal Error: GCM authentication tag read before encryption" end @auth_tag end # Generates (and memoizes) an OpenSSL::Cipher object and configures # it for the specified iv and encryption key using AEAD def openssl_encryptor @openssl_encryptor ||= begin encryptor = super encryptor.auth_data = '' encryptor end end # Encrypts, Base64 encodes +serialized_data+ and gets the authentication tag def encrypted_data @encrypted_data ||= begin enc_data_b64 = super @auth_tag = openssl_encryptor.auth_tag enc_data_b64 end end def self.encryptor_keys super + %w( auth_tag ) end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/check_encrypted.rb0000644000004100000410000000437712520074675024524 0ustar www-datawww-data# # Author:: Tyler Ball () # Copyright:: Copyright (c) 2010-2014 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/encrypted_data_bag_item/encryptor' class Chef::EncryptedDataBagItem # Common code for checking if a data bag appears encrypted module CheckEncrypted # Tries to autodetect if the item's raw hash appears to be encrypted. def encrypted?(raw_data) data = raw_data.reject { |k, _| k == "id" } # Remove the "id" key. # Assume hashes containing only the "id" key are not encrypted. # Otherwise, remove the keys that don't appear to be encrypted and compare # the result with the hash. If some entry has been removed, then some entry # doesn't appear to be encrypted and we assume the entire hash is not encrypted. data.empty? ? false : data.reject { |_, v| !looks_like_encrypted?(v) } == data end private # Checks if data looks like it has been encrypted by # Chef::EncryptedDataBagItem::Encryptor::VersionXEncryptor. Returns # true only when there is an exact match between the VersionXEncryptor # keys and the hash's keys. def looks_like_encrypted?(data) return false unless data.is_a?(Hash) && data.has_key?("version") case data["version"] when 1 Chef::EncryptedDataBagItem::Encryptor::Version1Encryptor.encryptor_keys.sort == data.keys.sort when 2 Chef::EncryptedDataBagItem::Encryptor::Version2Encryptor.encryptor_keys.sort == data.keys.sort when 3 Chef::EncryptedDataBagItem::Encryptor::Version3Encryptor.encryptor_keys.sort == data.keys.sort else false # version means something else... assume not encrypted. end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/decryption_failure.rb0000644000004100000410000000140012520074675025241 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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. # class Chef::EncryptedDataBagItem class DecryptionFailure < StandardError end end chef-12.3.0/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb0000644000004100000410000000236312520074675030452 0ustar www-datawww-data# # Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2014 Onddo Labs, SL. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef::EncryptedDataBagItem class EncryptedDataBagRequirementsFailure < StandardError end module Assertions def assert_requirements_met! unless OpenSSL::Cipher.method_defined?(:auth_data=) raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 2.0" end unless OpenSSL::Cipher.ciphers.include?(algorithm) raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support" end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/decryptor.rb0000644000004100000410000001545112520074675023400 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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 'yaml' require 'chef/json_compat' require 'openssl' require 'base64' require 'digest/sha2' require 'chef/encrypted_data_bag_item' require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format' require 'chef/encrypted_data_bag_item/decryption_failure' require 'chef/encrypted_data_bag_item/assertions' class Chef::EncryptedDataBagItem #=== Decryptor # For backwards compatibility, Chef implements decryption/deserialization for # older encrypted data bag item formats in addition to the current version. # Each decryption/deserialization strategy is implemented as a class in this # namespace. For convenience the factory method +Decryptor.for()+ can be used # to create an instance of the appropriate strategy for the given encrypted # data bag value. module Decryptor extend Chef::EncryptedDataBagItem::Assertions # Detects the encrypted data bag item format version and instantiates a # decryptor object for that version. Call #for_decrypted_item on the # resulting object to decrypt and deserialize it. def self.for(encrypted_value, key) format_version = format_version_of(encrypted_value) assert_format_version_acceptable!(format_version) case format_version when 3 Version3Decryptor.new(encrypted_value, key) when 2 Version2Decryptor.new(encrypted_value, key) when 1 Version1Decryptor.new(encrypted_value, key) when 0 Version0Decryptor.new(encrypted_value, key) else raise UnsupportedEncryptedDataBagItemFormat, "This version of chef does not support encrypted data bag item format version '#{format_version}'" end end def self.format_version_of(encrypted_value) if encrypted_value.respond_to?(:key?) encrypted_value["version"] else 0 end end class Version0Decryptor include Chef::EncryptedDataBagItem::Assertions attr_reader :encrypted_data attr_reader :key def initialize(encrypted_data, key) @encrypted_data = encrypted_data @key = key end # Returns the used decryption algorithm def algorithm ALGORITHM end def for_decrypted_item YAML.load(decrypted_data) end def decrypted_data @decrypted_data ||= begin plaintext = openssl_decryptor.update(encrypted_bytes) plaintext << openssl_decryptor.final rescue OpenSSL::Cipher::CipherError => e raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect" end end def encrypted_bytes Base64.decode64(@encrypted_data) end def openssl_decryptor @openssl_decryptor ||= begin d = OpenSSL::Cipher.new(algorithm) d.decrypt d.pkcs5_keyivgen(key) d end end end class Version1Decryptor < Version0Decryptor attr_reader :encrypted_data attr_reader :key def initialize(encrypted_data, key) @encrypted_data = encrypted_data @key = key end def for_decrypted_item Chef::JSONCompat.parse(decrypted_data)["json_wrapper"] rescue Chef::Exceptions::JSON::ParseError # convert to a DecryptionFailure error because the most likely scenario # here is that the decryption step was unsuccessful but returned bad # data rather than raising an error. raise DecryptionFailure, "Error decrypting data bag value. Most likely the provided key is incorrect" end def encrypted_bytes Base64.decode64(@encrypted_data["encrypted_data"]) end def iv Base64.decode64(@encrypted_data["iv"]) end def decrypted_data @decrypted_data ||= begin plaintext = openssl_decryptor.update(encrypted_bytes) plaintext << openssl_decryptor.final rescue OpenSSL::Cipher::CipherError => e raise DecryptionFailure, "Error decrypting data bag value: '#{e.message}'. Most likely the provided key is incorrect" end end def openssl_decryptor @openssl_decryptor ||= begin assert_valid_cipher!(@encrypted_data["cipher"], algorithm) d = OpenSSL::Cipher.new(algorithm) d.decrypt # We must set key before iv: https://bugs.ruby-lang.org/issues/8221 d.key = OpenSSL::Digest::SHA256.digest(key) d.iv = iv d end end end class Version2Decryptor < Version1Decryptor def decrypted_data validate_hmac! unless @decrypted_data super end def validate_hmac! digest = OpenSSL::Digest.new("sha256") raw_hmac = OpenSSL::HMAC.digest(digest, key, @encrypted_data["encrypted_data"]) if candidate_hmac_matches?(raw_hmac) true else raise DecryptionFailure, "Error decrypting data bag value: invalid hmac. Most likely the provided key is incorrect" end end private def candidate_hmac_matches?(expected_hmac) return false unless @encrypted_data["hmac"] expected_bytes = expected_hmac.bytes.to_a candidate_hmac_bytes = Base64.decode64(@encrypted_data["hmac"]).bytes.to_a valid = expected_bytes.size ^ candidate_hmac_bytes.size expected_bytes.zip(candidate_hmac_bytes) { |x, y| valid |= x ^ y.to_i } valid == 0 end end class Version3Decryptor < Version1Decryptor def initialize(encrypted_data, key) super assert_aead_requirements_met!(algorithm) end # Returns the used decryption algorithm def algorithm AEAD_ALGORITHM end def auth_tag auth_tag_b64 = @encrypted_data["auth_tag"] if auth_tag_b64.nil? raise DecryptionFailure, "Error decrypting data bag value: invalid authentication tag. Most likely the data is corrupted" end Base64.decode64(auth_tag_b64) end def openssl_decryptor @openssl_decryptor ||= begin d = super d.auth_tag = auth_tag d.auth_data = '' d end end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/encryption_failure.rb0000644000004100000410000000140512520074675025260 0ustar www-datawww-data# # Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2014 Onddo Labs, SL. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # class Chef::EncryptedDataBagItem class EncryptionFailure < StandardError end end chef-12.3.0/lib/chef/encrypted_data_bag_item/assertions.rb0000644000004100000410000000413012520074675023547 0ustar www-datawww-data# # Author:: Xabier de Zuazo () # Copyright:: Copyright (c) 2014 Onddo Labs, SL. # 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/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format' require 'chef/encrypted_data_bag_item/unsupported_cipher' class Chef::EncryptedDataBagItem class EncryptedDataBagRequirementsFailure < StandardError end module Assertions def assert_format_version_acceptable!(format_version) unless format_version.kind_of?(Integer) and format_version >= Chef::Config[:data_bag_decrypt_minimum_version] raise UnacceptableEncryptedDataBagItemFormat, "The encrypted data bag item has format version `#{format_version}', " + "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'" end end def assert_valid_cipher!(requested_cipher, algorithm) # In the future, chef may support configurable ciphers. For now, only # aes-256-cbc and aes-256-gcm are supported. unless requested_cipher == algorithm raise UnsupportedCipher, "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}', '#{AEAD_ALGORITHM}']" end end def assert_aead_requirements_met!(algorithm) unless OpenSSL::Cipher.ciphers.include?(algorithm) raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support" end end end end chef-12.3.0/lib/chef/encrypted_data_bag_item/unsupported_cipher.rb0000644000004100000410000000140012520074675025274 0ustar www-datawww-data# # Author:: Seth Falcon () # Copyright:: Copyright 2010-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. # class Chef::EncryptedDataBagItem class UnsupportedCipher < StandardError end end chef-12.3.0/lib/chef/dsl.rb0000644000004100000410000000031412520074675015322 0ustar www-datawww-datarequire 'chef/dsl/recipe' require 'chef/dsl/platform_introspection' require 'chef/dsl/data_query' require 'chef/dsl/include_recipe' require 'chef/dsl/include_attribute' require 'chef/dsl/registry_helper' chef-12.3.0/lib/chef/deprecation/0000755000004100000410000000000012520074675016512 5ustar www-datawww-datachef-12.3.0/lib/chef/deprecation/mixin/0000755000004100000410000000000012520074675017636 5ustar www-datawww-datachef-12.3.0/lib/chef/deprecation/mixin/template.rb0000644000004100000410000000275512520074675022007 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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 'tempfile' require 'erubis' class Chef module Deprecation module Mixin # == Deprecation::Provider::Mixin::Template # This module contains the deprecated functions of # Chef::Mixin::Template. These functions are refactored to different # components. They are frozen and will be removed in Chef 12. # module Template def render_template(template, context) begin eruby = Erubis::Eruby.new(template) output = eruby.evaluate(context) rescue Object => e raise TemplateError.new(e, template, context) end Tempfile.open("chef-rendered-template") do |tempfile| tempfile.print(output) tempfile.close yield tempfile end end end end end end chef-12.3.0/lib/chef/deprecation/provider/0000755000004100000410000000000012520074675020344 5ustar www-datawww-datachef-12.3.0/lib/chef/deprecation/provider/cookbook_file.rb0000644000004100000410000000325412520074675023502 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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. # class Chef module Deprecation module Provider # == Deprecation::Provider::CookbookFile # This module contains the deprecated functions of # Chef::Provider::CookbookFile. These functions are refactored to # different components. They are frozen and will be removed in Chef 12. # module CookbookFile def file_cache_location @file_cache_location ||= begin cookbook = run_context.cookbook_collection[resource_cookbook] cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path) end end def resource_cookbook @new_resource.cookbook || @new_resource.cookbook_name end def content_stale? ( ! ::File.exist?(@new_resource.path)) || ( ! compare_content) end def backup_new_resource if ::File.exists?(@new_resource.path) backup @new_resource.path end end end end end end chef-12.3.0/lib/chef/deprecation/provider/file.rb0000644000004100000410000001765512520074675021626 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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/util/path_helper' class Chef module Deprecation module Provider # == Deprecation::Provider::File # This module contains the deprecated functions of # Chef::Provider::File. These functions are refactored to different # components. They are frozen and will be removed in Chef 12. # module File def diff_current_from_content(new_content) result = nil Tempfile.open("chef-diff") do |file| file.write new_content file.close result = diff_current file.path end result end def is_binary?(path) ::File.open(path) do |file| buff = file.read(Chef::Config[:diff_filesize_threshold]) buff = "" if buff.nil? return buff !~ /^[\r[:print:]]*$/ end end def diff_current(temp_path) suppress_resource_reporting = false return [ "(diff output suppressed by config)" ] if Chef::Config[:diff_disabled] return [ "(no temp file with new content, diff output suppressed)" ] unless ::File.exists?(temp_path) # should never happen? # solaris does not support diff -N, so create tempfile to diff against if we are creating a new file target_path = if ::File.exists?(@current_resource.path) @current_resource.path else suppress_resource_reporting = true # suppress big diffs going to resource reporting service tempfile = Tempfile.new('chef-tempfile') tempfile.path end diff_filesize_threshold = Chef::Config[:diff_filesize_threshold] diff_output_threshold = Chef::Config[:diff_output_threshold] if ::File.size(target_path) > diff_filesize_threshold || ::File.size(temp_path) > diff_filesize_threshold return [ "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)" ] end # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs return [ "(current file is binary, diff output suppressed)"] if is_binary?(target_path) return [ "(new content is binary, diff output suppressed)"] if is_binary?(temp_path) begin # -u: Unified diff format result = shell_out("diff -u #{target_path} #{temp_path}" ) rescue Exception => e # Should *not* receive this, but in some circumstances it seems that # an exception can be thrown even using shell_out instead of shell_out! return [ "Could not determine diff. Error: #{e.message}" ] end # diff will set a non-zero return code even when there's # valid stdout results, if it encounters something unexpected # So as long as we have output, we'll show it. if not result.stdout.empty? if result.stdout.length > diff_output_threshold [ "(long diff of over #{diff_output_threshold} characters, diff output suppressed)" ] else val = result.stdout.split("\n") val.delete("\\ No newline at end of file") @new_resource.diff(val.join("\\n")) unless suppress_resource_reporting val end elsif not result.stderr.empty? [ "Could not determine diff. Error: #{result.stderr}" ] else [ "(no diff)" ] end end def setup_acl return if Chef::Platform.windows? acl_scanner = ScanAccessControl.new(@new_resource, @current_resource) acl_scanner.set_all! end def compare_content checksum(@current_resource.path) == new_resource_content_checksum end def set_content unless compare_content description = [] description << "update content in file #{@new_resource.path} from #{short_cksum(@current_resource.checksum)} to #{short_cksum(new_resource_content_checksum)}" description << diff_current_from_content(@new_resource.content) converge_by(description) do backup @new_resource.path if ::File.exists?(@new_resource.path) ::File.open(@new_resource.path, "w") {|f| f.write @new_resource.content } Chef::Log.info("#{@new_resource} contents updated") end end end def update_new_file_state(path=@new_resource.path) if !::File.directory?(path) @new_resource.checksum(checksum(path)) end if Chef::Platform.windows? # TODO: To work around CHEF-3554, add support for Windows # equivalent, or implicit resource reporting won't work for # Windows. return end acl_scanner = ScanAccessControl.new(@new_resource, @new_resource) acl_scanner.set_all! end def set_all_access_controls if access_controls.requires_changes? converge_by(access_controls.describe_changes) do access_controls.set_all #Update file state with new access values update_new_file_state end end end def deploy_tempfile Tempfile.open(::File.basename(@new_resource.name)) do |tempfile| yield tempfile temp_res = Chef::Resource::CookbookFile.new(@new_resource.name) temp_res.path(tempfile.path) ac = Chef::FileAccessControl.new(temp_res, @new_resource, self) ac.set_all! FileUtils.mv(tempfile.path, @new_resource.path) end end def backup(file=nil) file ||= @new_resource.path if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(file) time = Time.now savetime = time.strftime("%Y%m%d%H%M%S") backup_filename = "#{@new_resource.path}.chef-#{savetime}" backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows # if :file_backup_path is nil, we fallback to the old behavior of # keeping the backup in the same directory. We also need to to_s it # so we don't get a type error around implicit to_str conversions. prefix = Chef::Config[:file_backup_path].to_s backup_path = ::File.join(prefix, backup_filename) FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path] FileUtils.cp(file, backup_path, :preserve => true) Chef::Log.info("#{@new_resource} backed up to #{backup_path}") # Clean up after the number of backups slice_number = @new_resource.backup backup_files = Dir[Chef::Util::PathHelper.escape_glob(prefix, ".#{@new_resource.path}") + ".chef-*"].sort { |a,b| b <=> a } if backup_files.length >= @new_resource.backup remainder = backup_files.slice(slice_number..-1) remainder.each do |backup_to_delete| FileUtils.rm(backup_to_delete) Chef::Log.info("#{@new_resource} removed backup at #{backup_to_delete}") end end end end end end end end chef-12.3.0/lib/chef/deprecation/provider/remote_file.rb0000644000004100000410000000635412520074675023173 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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. # class Chef module Deprecation module Provider # == Deprecation::Provider::RemoteFile # This module contains the deprecated functions of # Chef::Provider::RemoteFile. These functions are refactored to different # components. They are frozen and will be removed in Chef 12. # module RemoteFile def current_resource_matches_target_checksum? @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{Regexp.escape(@new_resource.checksum)}/ end def matches_current_checksum?(candidate_file) Chef::Log.debug "#{@new_resource} checking for file existence of #{@new_resource.path}" if ::File.exists?(@new_resource.path) Chef::Log.debug "#{@new_resource} file exists at #{@new_resource.path}" @new_resource.checksum(checksum(candidate_file.path)) Chef::Log.debug "#{@new_resource} target checksum: #{@current_resource.checksum}" Chef::Log.debug "#{@new_resource} source checksum: #{@new_resource.checksum}" @new_resource.checksum == @current_resource.checksum else Chef::Log.debug "#{@new_resource} creating #{@new_resource.path}" false end end def backup_new_resource if ::File.exists?(@new_resource.path) Chef::Log.debug "#{@new_resource} checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}" backup @new_resource.path end end def source_file(source, current_checksum, &block) if absolute_uri?(source) fetch_from_uri(source, &block) elsif !Chef::Config[:solo] fetch_from_chef_server(source, current_checksum, &block) else fetch_from_local_cookbook(source, &block) end end def http_client_opts(source) opts={} # CHEF-3140 # 1. If it's already compressed, trying to compress it more will # probably be counter-productive. # 2. Some servers are misconfigured so that you GET $URL/file.tgz but # they respond with content type of tar and content encoding of gzip, # which tricks Chef::REST into decompressing the response body. In this # case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz, # which is not what you wanted. if @new_resource.path =~ /gz$/ or source =~ /gz$/ opts[:disable_gzip] = true end opts end end end end end chef-12.3.0/lib/chef/deprecation/provider/template.rb0000644000004100000410000000377512520074675022520 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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/deprecation/mixin/template' class Chef module Deprecation module Provider # == Deprecation::Provider::Template # This module contains the deprecated functions of # Chef::Provider::Template. These functions are refactored to different # components. They are frozen and will be removed in Chef 12. # module Template include Chef::Deprecation::Mixin::Template def template_finder @template_finder ||= begin Chef::Provider::TemplateFinder.new(run_context, cookbook_name, node) end end def template_location @template_file_cache_location ||= begin template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook) end end def resource_cookbook @new_resource.cookbook || @new_resource.cookbook_name end def rendered(rendered_template) @new_resource.checksum(checksum(rendered_template.path)) Chef::Log.debug("Current content's checksum: #{@current_resource.checksum}") Chef::Log.debug("Rendered content's checksum: #{@new_resource.checksum}") end def content_matches? @current_resource.checksum == @new_resource.checksum end end end end end chef-12.3.0/lib/chef/deprecation/warnings.rb0000644000004100000410000000241112520074675020665 0ustar www-datawww-data# # Author:: Serdar Sutay () # 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. # class Chef module Deprecation module Warnings def add_deprecation_warnings_for(method_names) method_names.each do |name| m = instance_method(name) define_method(name) do |*args| message = [] message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 12." message << "Please update your cookbooks accordingly. Accessed from:" caller[0..3].each {|l| message << l} Chef::Log.deprecation message super(*args) end end end end end end chef-12.3.0/lib/chef/null_logger.rb0000644000004100000410000000324512520074675017057 0ustar www-datawww-data# # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2014 Chef Software, Inc # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class Chef # Null logger implementation that just ignores everything. This is used by # classes that are intended to be reused outside of Chef, but need to offer # logging functionality when used by other Chef code. # # It does not define the full interface provided by Logger, just enough to be # a reasonable duck type. In particular, methods setting the log level, log # device, etc., are not implemented because any code calling those methods # probably expected a real logger and not this "fake" one. class NullLogger def fatal(message, &block) end def error(message, &block) end def warn(message, &block) end def info(message, &block) end def debug(message, &block) end def add(severity, message=nil, progname=nil) end def <<(message) end def fatal? false end def error? false end def warn? false end def info? false end def debug? false end end end chef-12.3.0/lib/chef/node/0000755000004100000410000000000012520074675015142 5ustar www-datawww-datachef-12.3.0/lib/chef/node/immutable_collections.rb0000644000004100000410000001313312520074675022045 0ustar www-datawww-data class Chef class Node module Immutablize def immutablize(value) case value when Hash ImmutableMash.new(value) when Array ImmutableArray.new(value) else value end end end # == ImmutableArray # ImmutableArray is used to implement Array collections when reading node # attributes. # # ImmutableArray acts like an ordinary Array, except: # * Methods that mutate the array are overridden to raise an error, making # the collection more or less immutable. # * Since this class stores values computed from a parent # Chef::Node::Attribute's values, it overrides all reader methods to # detect staleness and raise an error if accessed when stale. class ImmutableArray < Array include Immutablize alias :internal_push :<< private :internal_push # A list of methods that mutate Array. Each of these is overridden to # raise an error, making this instances of this class more or less # immutable. DISALLOWED_MUTATOR_METHODS = [ :<<, :[]=, :clear, :collect!, :compact!, :default=, :default_proc=, :delete, :delete_at, :delete_if, :fill, :flatten!, :insert, :keep_if, :map!, :merge!, :pop, :push, :update, :reject!, :reverse!, :replace, :select!, :shift, :slice!, :sort!, :sort_by!, :uniq!, :unshift ] def initialize(array_data) array_data.each do |value| internal_push(immutablize(value)) end end # Redefine all of the methods that mutate a Hash to raise an error when called. # This is the magic that makes this object "Immutable" DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| define_method(mutator_method_name) do |*args, &block| raise Exceptions::ImmutableAttributeModification end end # For elements like Fixnums, true, nil... def safe_dup(e) e.dup rescue TypeError e end def dup Array.new(map {|e| safe_dup(e)}) end def to_a a = Array.new each do |v| a << case v when ImmutableArray v.to_a when ImmutableMash v.to_hash else v end end a end end # == ImmutableMash # ImmutableMash implements Hash/Dict behavior for reading values from node # attributes. # # ImmutableMash acts like a Mash (Hash that is indifferent to String or # Symbol keys), with some important exceptions: # * Methods that mutate state are overridden to raise an error instead. # * Methods that read from the collection are overriden so that they check # if the Chef::Node::Attribute has been modified since an instance of # this class was generated. An error is raised if the object detects that # it is stale. # * Values can be accessed in attr_reader-like fashion via method_missing. class ImmutableMash < Mash include Immutablize alias :internal_set :[]= private :internal_set DISALLOWED_MUTATOR_METHODS = [ :[]=, :clear, :collect!, :default=, :default_proc=, :delete, :delete_if, :keep_if, :map!, :merge!, :update, :reject!, :replace, :select!, :shift ] def initialize(mash_data) mash_data.each do |key, value| internal_set(key, immutablize(value)) end end def public_method_that_only_deep_merge_should_use(key, value) internal_set(key, immutablize(value)) end alias :attribute? :has_key? # Redefine all of the methods that mutate a Hash to raise an error when called. # This is the magic that makes this object "Immutable" DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| define_method(mutator_method_name) do |*args, &block| raise Exceptions::ImmutableAttributeModification end end def method_missing(symbol, *args) if args.empty? if key?(symbol) self[symbol] else raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'" end # This will raise a ImmutableAttributeModification error: elsif symbol.to_s =~ /=$/ key_to_set = symbol.to_s[/^(.+)=$/, 1] self[key_to_set] = (args.length == 1 ? args[0] : args) else raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'" end end # Mash uses #convert_value to mashify values on input. # Since we're handling this ourselves, override it to be a no-op def convert_value(value) value end # NOTE: #default and #default= are likely to be pretty confusing. For a # regular ruby Hash, they control what value is returned for, e.g., # hash[:no_such_key] #=> hash.default # Of course, 'default' has a specific meaning in Chef-land def dup Mash.new(self) end def to_hash h = Hash.new each_pair do |k, v| h[k] = case v when ImmutableMash v.to_hash when ImmutableArray v.to_a else v end end h end end end end chef-12.3.0/lib/chef/node/attribute_collections.rb0000644000004100000410000002424212520074675022074 0ustar www-datawww-data#-- # Author:: Daniel DeLeo () # 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. # class Chef class Node # == AttrArray # AttrArray is identical to Array, except that it keeps a reference to the # "root" (Chef::Node::Attribute) object, and will trigger a cache # invalidation on that object when mutated. class AttrArray < Array MUTATOR_METHODS = [ :<<, :[]=, :clear, :collect!, :compact!, :default=, :default_proc=, :delete, :delete_at, :delete_if, :fill, :flatten!, :insert, :keep_if, :map!, :merge!, :pop, :push, :update, :reject!, :reverse!, :replace, :select!, :shift, :slice!, :sort!, :sort_by!, :uniq!, :unshift ] # For all of the methods that may mutate an Array, we override them to # also invalidate the cached merged_attributes on the root # Node::Attribute object. MUTATOR_METHODS.each do |mutator| define_method(mutator) do |*args, &block| root.reset_cache(root.top_level_breadcrumb) super(*args, &block) end end attr_reader :root def initialize(root, data) @root = root super(data) end # For elements like Fixnums, true, nil... def safe_dup(e) e.dup rescue TypeError e end def dup Array.new(map {|e| safe_dup(e)}) end end # == VividMash # VividMash is identical to a Mash, with a few exceptions: # * It has a reference to the root Chef::Node::Attribute to which it # belongs, and will trigger cache invalidation on that object when # mutated. # * It auto-vivifies, that is a reference to a missing element will result # in the creation of a new VividMash for that key. (This only works when # using the element reference method, `[]` -- other methods, such as # #fetch, work as normal). # * It supports a set_unless flag (via the root Attribute object) which # allows `||=` style behavior (`||=` does not work with # auto-vivification). This is only implemented for #[]=; methods such as # #store work as normal. # * attr_accessor style element set and get are supported via method_missing class VividMash < Mash attr_reader :root # Methods that mutate a VividMash. Each of them is overridden so that it # also invalidates the cached merged_attributes on the root Attribute # object. MUTATOR_METHODS = [ :clear, :delete, :delete_if, :keep_if, :merge!, :update, :reject!, :replace, :select!, :shift ] # For all of the mutating methods on Mash, override them so that they # also invalidate the cached `merged_attributes` on the root Attribute # object. MUTATOR_METHODS.each do |mutator| define_method(mutator) do |*args, &block| root.reset_cache(root.top_level_breadcrumb) super(*args, &block) end end def initialize(root, data={}) @root = root super(data) end def [](key) root.top_level_breadcrumb ||= key value = super if !key?(key) value = self.class.new(root) self[key] = value else value end end def []=(key, value) root.top_level_breadcrumb ||= key if set_unless? && key?(key) self[key] else root.reset_cache(root.top_level_breadcrumb) super end end alias :attribute? :has_key? def method_missing(symbol, *args) # Calling `puts arg` implicitly calls #to_ary on `arg`. If `arg` does # not implement #to_ary, ruby recognizes it as a single argument, and # if it returns an Array, then ruby prints each element. If we don't # account for that here, we'll auto-vivify a VividMash for the key # :to_ary which creates an unwanted key and raises a TypeError. if symbol == :to_ary super elsif args.empty? self[symbol] elsif symbol.to_s =~ /=$/ key_to_set = symbol.to_s[/^(.+)=$/, 1] self[key_to_set] = (args.length == 1 ? args[0] : args) else raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'. To set an attribute, use `#{symbol}=value' instead." end end def set_unless? @root.set_unless? end def convert_key(key) super end # Mash uses #convert_value to mashify values on input. # We override it here to convert hash or array values to VividMash or # AttrArray for consistency and to ensure that the added parts of the # attribute tree will have the correct cache invalidation behavior. def convert_value(value) case value when VividMash value when Hash VividMash.new(root, value) when Array AttrArray.new(root, value) else value end end def dup Mash.new(self) end end # == MultiMash # This is a Hash-like object that contains multiple VividMashes in it. Its # purpose is so that the user can descend into the mash and delete a subtree # from all of the Mash objects (used to delete all values in a subtree from # default, force_default, role_default and env_default at the same time). The # assignment operator strictly does assignment (does no merging) and works # by deleting the subtree and then assigning to the last mash which passed in # the initializer. # # A lot of the complexity of this class comes from the fact that at any key # value some or all of the mashes may walk off their ends and become nil or # true or something. The schema may change so that one precidence leve may # be 'true' object and another may be a VividMash. It is also possible that # one or many of them may transition from VividMashes to Hashes or Arrays. # # It also supports the case where you may be deleting a key using node.rm # in which case if intermediate keys all walk off into nil then you don't want # to be autovivifying keys as you go. On the other hand you may be using # node.force_default! in which case you'll wind up with a []= operator at the # end and you want autovivification, so we conditionally have to support either # operation. # # @todo: can we have an autovivify class that decorates a class that doesn't # autovivify or something so that the code is less awful? # class MultiMash attr_reader :root attr_reader :mashes attr_reader :opts attr_reader :primary_mash # Initialize with an array of mashes. For the delete return value to work # properly the mashes must come from the same attribute level (i.e. all # override or all default, but not a mix of both). def initialize(root, primary_mash, mashes, opts={}) @root = root @primary_mash = primary_mash @mashes = mashes @opts = opts @opts[:autovivify] = true if @opts[:autovivify].nil? end def [](key) # handle the secondary mashes new_mashes = [] mashes.each do |mash| new_mash = safe_evalute_key(mash, key) # secondary mashes never autovivify so once they fall into nil, we just stop tracking them new_mashes.push(new_mash) unless new_mash.nil? end new_primary_mash = safe_evalute_key(primary_mash, key) if new_primary_mash.nil? && @opts[:autovivify] primary_mash[key] = VividMash.new(root) new_primary_mash = primary_mash[key] end MultiMash.new(root, new_primary_mash, new_mashes, opts) end def []=(key, value) if primary_mash.nil? # This theoretically should never happen since node#force_default! setter methods will autovivify and # node#rm methods do not end in #[]= operators. raise TypeError, "No autovivification was specified initially on a method chain ending in assignment" end ret = delete(key) primary_mash[key] = value ret end # mash.element('foo', 'bar') is the same as mash['foo']['bar'] def element(key = nil, *subkeys) return self if key.nil? submash = self[key] subkeys.empty? ? submash : submash.element(*subkeys) end def delete(key) # the return value is a deep merge which is correct semantics when # merging between attributes on the same level (this would be incorrect # if passed both override and default attributes which would need hash_only # merging). ret = mashes.inject(Mash.new) do |merged, mash| Chef::Mixin::DeepMerge.merge(merged, mash) end ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash) mashes.each do |mash| mash.delete(key) if mash.respond_to?(:delete) end primary_mash.delete(key) if primary_mash.respond_to?(:delete) ret[key] end private def safe_evalute_key(mash, key) if mash.respond_to?(:[]) if mash.respond_to?(:has_key?) if mash.has_key?(key) return mash[key] if mash[key].respond_to?(:[]) end elsif !mash[key].nil? return mash[key] if mash[key].respond_to?(:[]) end end return nil end end end end chef-12.3.0/lib/chef/node/attribute.rb0000644000004100000410000004427712520074675017510 0ustar www-datawww-data#-- # Author:: Adam Jacob () # Author:: AJ Christensen () # 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 'chef/node/immutable_collections' require 'chef/node/attribute_collections' require 'chef/mixin/deep_merge' require 'chef/log' class Chef class Node # == Attribute # Attribute implements a nested key-value (Hash) and flat collection # (Array) data structure supporting multiple levels of precedence, such # that a given key may have multiple values internally, but will only # return the highest precedence value when reading. class Attribute < Mash include Immutablize include Enumerable # List of the component attribute hashes, in order of precedence, low to # high. COMPONENTS = [ :@default, :@env_default, :@role_default, :@force_default, :@normal, :@override, :@role_override, :@env_override, :@force_override, :@automatic ].freeze DEFAULT_COMPONENTS = [ :@default, :@env_default, :@role_default, :@force_default ] OVERRIDE_COMPONENTS = [ :@override, :@role_override, :@env_override, :@force_override ] [:all?, :any?, :assoc, :chunk, :collect, :collect_concat, :compare_by_identity, :compare_by_identity?, :count, :cycle, :detect, :drop, :drop_while, :each, :each_cons, :each_entry, :each_key, :each_pair, :each_slice, :each_value, :each_with_index, :each_with_object, :empty?, :entries, :except, :fetch, :find, :find_all, :find_index, :first, :flat_map, :flatten, :grep, :group_by, :has_value?, :include?, :index, :inject, :invert, :key, :keys, :length, :map, :max, :max_by, :merge, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :partition, :rassoc, :reduce, :reject, :reverse_each, :select, :size, :slice_before, :sort, :sort_by, :store, :symbolize_keys, :take, :take_while, :to_a, :to_hash, :to_set, :value?, :values, :values_at, :zip].each do |delegated_method| define_method(delegated_method) do |*args, &block| merged_attributes.send(delegated_method, *args, &block) end end # return the cookbook level default attribute component attr_reader :default # return the role level default attribute component attr_reader :role_default # return the environment level default attribute component attr_reader :env_default # return the force_default level attribute component attr_reader :force_default # return the "normal" level attribute component attr_reader :normal # return the cookbook level override attribute component attr_reader :override # return the role level override attribute component attr_reader :role_override # return the enviroment level override attribute component attr_reader :env_override # return the force override level attribute component attr_reader :force_override # return the automatic level attribute component attr_reader :automatic # This is used to track the top level key as we descend through method chaining into # a precedence level (e.g. node.default['foo']['bar']['baz']= results in 'foo' here). We # need this so that when we hit the end of a method chain which results in a mutator method # that we can invalidate the whole top-level deep merge cache for the top-level key. It is # the responsibility of the accessor on the Chef::Node object to reset this to nil, and then # the first VividMash#[] call can ||= and set this to the first key we encounter. attr_accessor :top_level_breadcrumb # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the # top-level keys of the node object, and we save the computed deep-merge for that key here. There is # no cache of subtrees. attr_accessor :deep_merge_cache def initialize(normal, default, override, automatic) @set_unless_present = false @default = VividMash.new(self, default) @env_default = VividMash.new(self, {}) @role_default = VividMash.new(self, {}) @force_default = VividMash.new(self, {}) @normal = VividMash.new(self, normal) @override = VividMash.new(self, override) @role_override = VividMash.new(self, {}) @env_override = VividMash.new(self, {}) @force_override = VividMash.new(self, {}) @automatic = VividMash.new(self, automatic) @merged_attributes = nil @combined_override = nil @combined_default = nil @top_level_breadcrumb = nil @deep_merge_cache = {} end # Debug what's going on with an attribute. +args+ is a path spec to the # attribute you're interested in. For example, to debug where the value # of `node[:network][:default_interface]` is coming from, use: # debug_value(:network, :default_interface). # The return value is an Array of Arrays. The first element is # `["set_unless_enabled?", Boolean]`, which describes whether the # attribute collection is in "set_unless" mode. The rest of the Arrays # are pairs of `["precedence_level", value]`, where precedence level is # the component, such as role default, normal, etc. and value is the # attribute value set at that precedence level. If there is no value at # that precedence level, +value+ will be the symbol +:not_present+. def debug_value(*args) components = COMPONENTS.map do |component| ivar = instance_variable_get(component) value = args.inject(ivar) do |so_far, key| if so_far == :not_present :not_present elsif so_far.has_key?(key) so_far[key] else :not_present end end [component.to_s.sub(/^@/,""), value] end [["set_unless_enabled?", @set_unless_present]] + components end # Enables or disables `||=`-like attribute setting. See, e.g., Node#set_unless def set_unless_value_present=(setting) @set_unless_present = setting end # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']= # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call # must invalidate the entire cache and re-deep-merge the entire node object. def reset_cache(path = nil) if path.nil? @deep_merge_cache = {} else deep_merge_cache.delete(path.to_s) end end alias :reset :reset_cache # Set the cookbook level default attribute component to +new_data+. def default=(new_data) reset @default = VividMash.new(self, new_data) end # Set the role level default attribute component to +new_data+ def role_default=(new_data) reset @role_default = VividMash.new(self, new_data) end # Set the environment level default attribute component to +new_data+ def env_default=(new_data) reset @env_default = VividMash.new(self, new_data) end # Set the force_default (+default!+) level attributes to +new_data+ def force_default=(new_data) reset @force_default = VividMash.new(self, new_data) end # Set the normal level attribute component to +new_data+ def normal=(new_data) reset @normal = VividMash.new(self, new_data) end # Set the cookbook level override attribute component to +new_data+ def override=(new_data) reset @override = VividMash.new(self, new_data) end # Set the role level override attribute component to +new_data+ def role_override=(new_data) reset @role_override = VividMash.new(self, new_data) end # Set the environment level override attribute component to +new_data+ def env_override=(new_data) reset @env_override = VividMash.new(self, new_data) end def force_override=(new_data) reset @force_override = VividMash.new(self, new_data) end def automatic=(new_data) reset @automatic = VividMash.new(self, new_data) end # # Deleting attributes # # clears attributes from all precedence levels def rm(*args) reset(args[0]) # just easier to compute our retval, rather than collect+merge sub-retvals ret = args.inject(merged_attributes) do |attr, arg| if attr.nil? || !attr.respond_to?(:[]) nil else begin attr[arg] rescue TypeError raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)" end end end rm_default(*args) rm_normal(*args) rm_override(*args) ret end # does ['foo']['bar'].delete('baz') def remove_from_precedence_level(level, *args, key) multimash = level.element(*args) multimash.nil? ? nil : multimash.delete(key) end private :remove_from_precedence_level # clears attributes from all default precedence levels # # equivalent to: force_default!['foo']['bar'].delete('baz') def rm_default(*args) reset(args[0]) remove_from_precedence_level(force_default!(autovivify: false), *args) end # clears attributes from normal precedence # # equivalent to: normal!['foo']['bar'].delete('baz') def rm_normal(*args) reset(args[0]) remove_from_precedence_level(normal!(autovivify: false), *args) end # clears attributes from all override precedence levels # # equivalent to: force_override!['foo']['bar'].delete('baz') def rm_override(*args) reset(args[0]) remove_from_precedence_level(force_override!(autovivify: false), *args) end # # Replacing attributes without merging # # sets default attributes without merging def default!(opts={}) # FIXME: do not flush whole cache reset MultiMash.new(self, @default, [], opts) end # sets normal attributes without merging def normal!(opts={}) # FIXME: do not flush whole cache reset MultiMash.new(self, @normal, [], opts) end # sets override attributes without merging def override!(opts={}) # FIXME: do not flush whole cache reset MultiMash.new(self, @override, [], opts) end # clears from all default precedence levels and then sets force_default def force_default!(opts={}) # FIXME: do not flush whole cache reset MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts) end # clears from all override precedence levels and then sets force_override def force_override!(opts={}) # FIXME: do not flush whole cache reset MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts) end # # Accessing merged attributes. # # Note that merged_attributes('foo', 'bar', 'baz') can be called to compute only the # deep merge of node['foo']['bar']['baz'], but in practice we currently always compute # all of node['foo'] even if the user only requires node['foo']['bar']['baz']. # def merged_attributes(*path) # immutablize( merge_all(path) # ) end def combined_override(*path) immutablize(merge_overrides(path)) end def combined_default(*path) immutablize(merge_defaults(path)) end def [](key) if deep_merge_cache.has_key?(key.to_s) # return the cache of the deep merged values by top-level key deep_merge_cache[key.to_s] else # save all the work of computing node[key] deep_merge_cache[key.to_s] = merged_attributes(key) end end def []=(key, value) raise Exceptions::ImmutableAttributeModification end def has_key?(key) COMPONENTS.any? do |component_ivar| instance_variable_get(component_ivar).has_key?(key) end end alias :attribute? :has_key? alias :member? :has_key? alias :include? :has_key? alias :key? :has_key? alias :each_attribute :each def method_missing(symbol, *args) if args.empty? if key?(symbol) self[symbol] else raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'" end elsif symbol.to_s =~ /=$/ key_to_set = symbol.to_s[/^(.+)=$/, 1] self[key_to_set] = (args.length == 1 ? args[0] : args) else raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'" end end def to_s merged_attributes.to_s end def inspect "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map{|iv| "#{iv}=#{instance_variable_get(iv).inspect}" }.join(', ') << ">" end def set_unless? @set_unless_present end private # Helper method for merge_all/merge_defaults/merge_overrides. # # apply_path(thing, [ "foo", "bar", "baz" ]) = thing["foo"]["bar"]["baz"] # # The path value can be nil in which case the whole component is returned. # # It returns nil (does not raise an exception) if it walks off the end of an Mash/Hash/Array, it does not # raise any TypeError if it attempts to apply a hash key to an Integer/String/TrueClass, and just returns # nil in that case. # def apply_path(component, path) path ||= [] path.inject(component) do |val, path_arg| if val.respond_to?(:[]) # Have an Array-like or Hash-like thing if !val.respond_to?(:has_key?) # Have an Array-like thing val[path_arg] elsif val.has_key?(path_arg) # Hash-like thing (must check has_key? first to protect against Autovivification) val[path_arg] else nil end else nil end end end # For elements like Fixnums, true, nil... def safe_dup(e) e.dup rescue TypeError e end # Deep merge all attribute levels using hash-only merging between different precidence # levels (so override arrays completely replace arrays set at any default level). # # The path allows for selectively deep-merging a subtree of the node object. # # @param path [Array] Array of args to method chain to descend into the node object # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object def merge_all(path) components = [ merge_defaults(path), apply_path(@normal, path), merge_overrides(path), apply_path(@automatic, path), ] components.map! do |component| safe_dup(component) end return nil if components.compact.empty? components.inject(ImmutableMash.new({})) do |merged, component| Chef::Mixin::DeepMerge.hash_only_merge!(merged, component) end end # Deep merge the default attribute levels with array merging. # # The path allows for selectively deep-merging a subtree of the node object. # # @param path [Array] Array of args to method chain to descend into the node object # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object def merge_defaults(path) DEFAULT_COMPONENTS.inject(nil) do |merged, component_ivar| component_value = apply_path(instance_variable_get(component_ivar), path) Chef::Mixin::DeepMerge.deep_merge(component_value, merged) end end # Deep merge the override attribute levels with array merging. # # The path allows for selectively deep-merging a subtree of the node object. # # @param path [Array] Array of args to method chain to descend into the node object # @return [attr] Deep Merged values (may be VividMash, Hash, Array, etc) from the node object def merge_overrides(path) OVERRIDE_COMPONENTS.inject(nil) do |merged, component_ivar| component_value = apply_path(instance_variable_get(component_ivar), path) Chef::Mixin::DeepMerge.deep_merge(component_value, merged) end end end end end chef-12.3.0/lib/chef.rb0000644000004100000410000000200312520074675014535 0ustar www-datawww-data# # Author:: Adam Jacob () # 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 'chef/version' require 'chef/nil_argument' require 'chef/mash' require 'chef/exceptions' require 'chef/log' require 'chef/config' require 'chef/providers' require 'chef/resources' require 'chef/shell_out' require 'chef/daemon' require 'chef/run_status' require 'chef/handler' require 'chef/handler/json_file' require 'chef/chef_class' chef-12.3.0/metadata.yml0000644000004100000410000023575212520074675015063 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: chef version: !ruby/object:Gem::Version version: 12.3.0 platform: ruby authors: - Adam Jacob autorequire: bindir: bin cert_chain: [] date: 2015-04-28 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: mixlib-config requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.0' - !ruby/object:Gem::Dependency name: mixlib-cli requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.4' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.4' - !ruby/object:Gem::Dependency name: mixlib-log requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: mixlib-authentication requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: mixlib-shellout requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.0.0.rc.0 - - "<" - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.0.0.rc.0 - - "<" - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: ohai requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '8.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '8.0' - !ruby/object:Gem::Dependency name: ffi-yajl requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '1.2' - - "<" - !ruby/object:Gem::Version version: '3.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '1.2' - - "<" - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: net-ssh requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.6' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.6' - !ruby/object:Gem::Dependency name: net-ssh-multi requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.1' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.1' - !ruby/object:Gem::Dependency name: highline requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.6' - - ">=" - !ruby/object:Gem::Version version: 1.6.9 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.6' - - ">=" - !ruby/object:Gem::Version version: 1.6.9 - !ruby/object:Gem::Dependency name: erubis requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.7' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.7' - !ruby/object:Gem::Dependency name: diff-lcs requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' - - ">=" - !ruby/object:Gem::Version version: 1.2.4 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' - - ">=" - !ruby/object:Gem::Version version: 1.2.4 - !ruby/object:Gem::Dependency name: chef-zero requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.1' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.1' - !ruby/object:Gem::Dependency name: pry requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.9' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '0.9' - !ruby/object:Gem::Dependency name: plist requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.1.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.1.0 - !ruby/object:Gem::Dependency name: rspec-core requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: rspec-expectations requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: rspec-mocks requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: rspec_junit_formatter requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 0.2.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 0.2.0 - !ruby/object:Gem::Dependency name: serverspec requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.7' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.7' - !ruby/object:Gem::Dependency name: specinfra requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.10' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '2.10' - !ruby/object:Gem::Dependency name: rack requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 10.1.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 10.1.0 description: A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure. email: adam@getchef.com executables: - chef-client - chef-solo - knife - chef-shell - chef-apply extensions: [] extra_rdoc_files: - README.md - CONTRIBUTING.md - LICENSE files: - CONTRIBUTING.md - LICENSE - README.md - Rakefile - bin/chef-apply - bin/chef-client - bin/chef-shell - bin/chef-solo - bin/knife - distro/common/html/_sources/ctl_chef_client.txt - distro/common/html/_sources/ctl_chef_server.txt - distro/common/html/_sources/ctl_chef_shell.txt - distro/common/html/_sources/ctl_chef_solo.txt - distro/common/html/_sources/index.txt - distro/common/html/_sources/knife.txt - distro/common/html/_sources/knife_bootstrap.txt - distro/common/html/_sources/knife_client.txt - distro/common/html/_sources/knife_common_options.txt - distro/common/html/_sources/knife_configure.txt - distro/common/html/_sources/knife_cookbook.txt - distro/common/html/_sources/knife_cookbook_site.txt - distro/common/html/_sources/knife_data_bag.txt - distro/common/html/_sources/knife_delete.txt - distro/common/html/_sources/knife_deps.txt - distro/common/html/_sources/knife_diff.txt - distro/common/html/_sources/knife_download.txt - distro/common/html/_sources/knife_edit.txt - distro/common/html/_sources/knife_environment.txt - distro/common/html/_sources/knife_exec.txt - distro/common/html/_sources/knife_index_rebuild.txt - distro/common/html/_sources/knife_list.txt - distro/common/html/_sources/knife_node.txt - distro/common/html/_sources/knife_raw.txt - distro/common/html/_sources/knife_recipe_list.txt - distro/common/html/_sources/knife_role.txt - distro/common/html/_sources/knife_search.txt - distro/common/html/_sources/knife_serve.txt - distro/common/html/_sources/knife_show.txt - distro/common/html/_sources/knife_ssh.txt - distro/common/html/_sources/knife_ssl_check.txt - distro/common/html/_sources/knife_ssl_fetch.txt - distro/common/html/_sources/knife_status.txt - distro/common/html/_sources/knife_tag.txt - distro/common/html/_sources/knife_upload.txt - distro/common/html/_sources/knife_user.txt - distro/common/html/_sources/knife_using.txt - distro/common/html/_sources/knife_xargs.txt - distro/common/html/_static/ajax-loader.gif - distro/common/html/_static/basic.css - distro/common/html/_static/chef.ico - distro/common/html/_static/chef_html_logo.png - distro/common/html/_static/comment-bright.png - distro/common/html/_static/comment-close.png - distro/common/html/_static/comment.png - distro/common/html/_static/contents.png - distro/common/html/_static/doctools.js - distro/common/html/_static/down-pressed.png - distro/common/html/_static/down.png - distro/common/html/_static/file.png - distro/common/html/_static/guide.css - distro/common/html/_static/jquery.js - distro/common/html/_static/minus.png - distro/common/html/_static/navigation.png - distro/common/html/_static/plus.png - distro/common/html/_static/pygments.css - distro/common/html/_static/searchtools.js - distro/common/html/_static/underscore.js - distro/common/html/_static/up-pressed.png - distro/common/html/_static/up.png - distro/common/html/_static/websupport.js - distro/common/html/ctl_chef_client.html - distro/common/html/ctl_chef_server.html - distro/common/html/ctl_chef_shell.html - distro/common/html/ctl_chef_solo.html - distro/common/html/index.html - distro/common/html/knife.html - distro/common/html/knife_bootstrap.html - distro/common/html/knife_client.html - distro/common/html/knife_common_options.html - distro/common/html/knife_configure.html - distro/common/html/knife_cookbook.html - distro/common/html/knife_cookbook_site.html - distro/common/html/knife_data_bag.html - distro/common/html/knife_delete.html - distro/common/html/knife_deps.html - distro/common/html/knife_diff.html - distro/common/html/knife_download.html - distro/common/html/knife_edit.html - distro/common/html/knife_environment.html - distro/common/html/knife_exec.html - distro/common/html/knife_index_rebuild.html - distro/common/html/knife_list.html - distro/common/html/knife_node.html - distro/common/html/knife_raw.html - distro/common/html/knife_recipe_list.html - distro/common/html/knife_role.html - distro/common/html/knife_search.html - distro/common/html/knife_serve.html - distro/common/html/knife_show.html - distro/common/html/knife_ssh.html - distro/common/html/knife_ssl_check.html - distro/common/html/knife_ssl_fetch.html - distro/common/html/knife_status.html - distro/common/html/knife_tag.html - distro/common/html/knife_upload.html - distro/common/html/knife_user.html - distro/common/html/knife_using.html - distro/common/html/knife_xargs.html - distro/common/html/objects.inv - distro/common/html/search.html - distro/common/html/searchindex.js - distro/common/man/man1/README.md - distro/common/man/man1/chef-shell.1 - distro/common/man/man1/knife-bootstrap.1 - distro/common/man/man1/knife-client.1 - distro/common/man/man1/knife-configure.1 - distro/common/man/man1/knife-cookbook-site.1 - distro/common/man/man1/knife-cookbook.1 - distro/common/man/man1/knife-data-bag.1 - distro/common/man/man1/knife-delete.1 - distro/common/man/man1/knife-deps.1 - distro/common/man/man1/knife-diff.1 - distro/common/man/man1/knife-download.1 - distro/common/man/man1/knife-edit.1 - distro/common/man/man1/knife-environment.1 - distro/common/man/man1/knife-exec.1 - distro/common/man/man1/knife-index-rebuild.1 - distro/common/man/man1/knife-list.1 - distro/common/man/man1/knife-node.1 - distro/common/man/man1/knife-raw.1 - distro/common/man/man1/knife-recipe-list.1 - distro/common/man/man1/knife-role.1 - distro/common/man/man1/knife-search.1 - distro/common/man/man1/knife-serve.1 - distro/common/man/man1/knife-show.1 - distro/common/man/man1/knife-ssh.1 - distro/common/man/man1/knife-ssl-check.1 - distro/common/man/man1/knife-ssl-fetch.1 - distro/common/man/man1/knife-status.1 - distro/common/man/man1/knife-tag.1 - distro/common/man/man1/knife-upload.1 - distro/common/man/man1/knife-user.1 - distro/common/man/man1/knife-xargs.1 - distro/common/man/man1/knife.1 - distro/common/man/man8/chef-apply.8 - distro/common/man/man8/chef-client.8 - distro/common/man/man8/chef-solo.8 - distro/common/markdown/README - distro/common/markdown/man1/chef-shell.mkd - distro/common/markdown/man1/knife-bootstrap.mkd - distro/common/markdown/man1/knife-client.mkd - distro/common/markdown/man1/knife-configure.mkd - distro/common/markdown/man1/knife-cookbook-site.mkd - distro/common/markdown/man1/knife-cookbook.mkd - distro/common/markdown/man1/knife-data-bag.mkd - distro/common/markdown/man1/knife-environment.mkd - distro/common/markdown/man1/knife-exec.mkd - distro/common/markdown/man1/knife-index.mkd - distro/common/markdown/man1/knife-node.mkd - distro/common/markdown/man1/knife-role.mkd - distro/common/markdown/man1/knife-search.mkd - distro/common/markdown/man1/knife-ssh.mkd - distro/common/markdown/man1/knife-status.mkd - distro/common/markdown/man1/knife-tag.mkd - distro/common/markdown/man1/knife.mkd - distro/common/markdown/man8/chef-client.mkd - distro/common/markdown/man8/chef-expander.mkd - distro/common/markdown/man8/chef-expanderctl.mkd - distro/common/markdown/man8/chef-server-webui.mkd - distro/common/markdown/man8/chef-server.mkd - distro/common/markdown/man8/chef-solo.mkd - distro/common/markdown/man8/chef-solr.mkd - lib/chef.rb - lib/chef/api_client.rb - lib/chef/api_client/registration.rb - lib/chef/application.rb - lib/chef/application/apply.rb - lib/chef/application/client.rb - lib/chef/application/knife.rb - lib/chef/application/solo.rb - lib/chef/application/windows_service.rb - lib/chef/application/windows_service_manager.rb - lib/chef/applications.rb - lib/chef/audit/audit_event_proxy.rb - lib/chef/audit/audit_reporter.rb - lib/chef/audit/control_group_data.rb - lib/chef/audit/rspec_formatter.rb - lib/chef/audit/runner.rb - lib/chef/chef_class.rb - lib/chef/chef_fs.rb - lib/chef/chef_fs/chef_fs_data_store.rb - lib/chef/chef_fs/command_line.rb - lib/chef/chef_fs/config.rb - lib/chef/chef_fs/data_handler/acl_data_handler.rb - lib/chef/chef_fs/data_handler/client_data_handler.rb - lib/chef/chef_fs/data_handler/container_data_handler.rb - lib/chef/chef_fs/data_handler/cookbook_data_handler.rb - lib/chef/chef_fs/data_handler/data_bag_item_data_handler.rb - lib/chef/chef_fs/data_handler/data_handler_base.rb - lib/chef/chef_fs/data_handler/environment_data_handler.rb - lib/chef/chef_fs/data_handler/group_data_handler.rb - lib/chef/chef_fs/data_handler/node_data_handler.rb - lib/chef/chef_fs/data_handler/organization_data_handler.rb - lib/chef/chef_fs/data_handler/organization_invites_data_handler.rb - lib/chef/chef_fs/data_handler/organization_members_data_handler.rb - lib/chef/chef_fs/data_handler/policy_data_handler.rb - lib/chef/chef_fs/data_handler/role_data_handler.rb - lib/chef/chef_fs/data_handler/user_data_handler.rb - lib/chef/chef_fs/file_pattern.rb - lib/chef/chef_fs/file_system.rb - lib/chef/chef_fs/file_system/acl_dir.rb - lib/chef/chef_fs/file_system/acl_entry.rb - lib/chef/chef_fs/file_system/acls_dir.rb - lib/chef/chef_fs/file_system/already_exists_error.rb - lib/chef/chef_fs/file_system/base_fs_dir.rb - lib/chef/chef_fs/file_system/base_fs_object.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_acls_dir.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_entry.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_data_bags_dir.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb - lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb - lib/chef/chef_fs/file_system/chef_server_root_dir.rb - lib/chef/chef_fs/file_system/cookbook_dir.rb - lib/chef/chef_fs/file_system/cookbook_file.rb - lib/chef/chef_fs/file_system/cookbook_frozen_error.rb - lib/chef/chef_fs/file_system/cookbook_subdir.rb - lib/chef/chef_fs/file_system/cookbooks_acl_dir.rb - lib/chef/chef_fs/file_system/cookbooks_dir.rb - lib/chef/chef_fs/file_system/data_bag_dir.rb - lib/chef/chef_fs/file_system/data_bags_dir.rb - lib/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb - lib/chef/chef_fs/file_system/environments_dir.rb - lib/chef/chef_fs/file_system/file_system_entry.rb - lib/chef/chef_fs/file_system/file_system_error.rb - lib/chef/chef_fs/file_system/file_system_root_dir.rb - lib/chef/chef_fs/file_system/memory_dir.rb - lib/chef/chef_fs/file_system/memory_file.rb - lib/chef/chef_fs/file_system/memory_root.rb - lib/chef/chef_fs/file_system/multiplexed_dir.rb - lib/chef/chef_fs/file_system/must_delete_recursively_error.rb - lib/chef/chef_fs/file_system/nodes_dir.rb - lib/chef/chef_fs/file_system/nonexistent_fs_object.rb - lib/chef/chef_fs/file_system/not_found_error.rb - lib/chef/chef_fs/file_system/operation_failed_error.rb - lib/chef/chef_fs/file_system/operation_not_allowed_error.rb - lib/chef/chef_fs/file_system/org_entry.rb - lib/chef/chef_fs/file_system/organization_invites_entry.rb - lib/chef/chef_fs/file_system/organization_members_entry.rb - lib/chef/chef_fs/file_system/rest_list_dir.rb - lib/chef/chef_fs/file_system/rest_list_entry.rb - lib/chef/chef_fs/knife.rb - lib/chef/chef_fs/parallelizer.rb - lib/chef/chef_fs/parallelizer/flatten_enumerable.rb - lib/chef/chef_fs/parallelizer/parallel_enumerable.rb - lib/chef/chef_fs/path_utils.rb - lib/chef/client.rb - lib/chef/config.rb - lib/chef/config_fetcher.rb - lib/chef/cookbook/chefignore.rb - lib/chef/cookbook/cookbook_collection.rb - lib/chef/cookbook/cookbook_version_loader.rb - lib/chef/cookbook/file_system_file_vendor.rb - lib/chef/cookbook/file_vendor.rb - lib/chef/cookbook/metadata.rb - lib/chef/cookbook/remote_file_vendor.rb - lib/chef/cookbook/synchronizer.rb - lib/chef/cookbook/syntax_check.rb - lib/chef/cookbook_loader.rb - lib/chef/cookbook_manifest.rb - lib/chef/cookbook_site_streaming_uploader.rb - lib/chef/cookbook_uploader.rb - lib/chef/cookbook_version.rb - lib/chef/daemon.rb - lib/chef/data_bag.rb - lib/chef/data_bag_item.rb - lib/chef/deprecation/mixin/template.rb - lib/chef/deprecation/provider/cookbook_file.rb - lib/chef/deprecation/provider/file.rb - lib/chef/deprecation/provider/remote_file.rb - lib/chef/deprecation/provider/template.rb - lib/chef/deprecation/warnings.rb - lib/chef/digester.rb - lib/chef/dsl.rb - lib/chef/dsl/audit.rb - lib/chef/dsl/data_query.rb - lib/chef/dsl/include_attribute.rb - lib/chef/dsl/include_recipe.rb - lib/chef/dsl/platform_introspection.rb - lib/chef/dsl/powershell.rb - lib/chef/dsl/reboot_pending.rb - lib/chef/dsl/recipe.rb - lib/chef/dsl/registry_helper.rb - lib/chef/encrypted_data_bag_item.rb - lib/chef/encrypted_data_bag_item/assertions.rb - lib/chef/encrypted_data_bag_item/check_encrypted.rb - lib/chef/encrypted_data_bag_item/decryption_failure.rb - lib/chef/encrypted_data_bag_item/decryptor.rb - lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb - lib/chef/encrypted_data_bag_item/encryption_failure.rb - lib/chef/encrypted_data_bag_item/encryptor.rb - lib/chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format.rb - lib/chef/encrypted_data_bag_item/unsupported_cipher.rb - lib/chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format.rb - lib/chef/environment.rb - lib/chef/event_dispatch/base.rb - lib/chef/event_dispatch/dispatcher.rb - lib/chef/event_dispatch/events_output_stream.rb - lib/chef/event_loggers/base.rb - lib/chef/event_loggers/windows_eventlog.rb - lib/chef/exceptions.rb - lib/chef/file_access_control.rb - lib/chef/file_access_control/unix.rb - lib/chef/file_access_control/windows.rb - lib/chef/file_cache.rb - lib/chef/file_content_management/content_base.rb - lib/chef/file_content_management/deploy.rb - lib/chef/file_content_management/deploy/cp.rb - lib/chef/file_content_management/deploy/mv_unix.rb - lib/chef/file_content_management/deploy/mv_windows.rb - lib/chef/file_content_management/tempfile.rb - lib/chef/formatters/base.rb - lib/chef/formatters/doc.rb - lib/chef/formatters/error_descriptor.rb - lib/chef/formatters/error_inspectors.rb - lib/chef/formatters/error_inspectors/api_error_formatting.rb - lib/chef/formatters/error_inspectors/compile_error_inspector.rb - lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb - lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb - lib/chef/formatters/error_inspectors/node_load_error_inspector.rb - lib/chef/formatters/error_inspectors/registration_error_inspector.rb - lib/chef/formatters/error_inspectors/resource_failure_inspector.rb - lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb - lib/chef/formatters/error_mapper.rb - lib/chef/formatters/indentable_output_stream.rb - lib/chef/formatters/minimal.rb - lib/chef/guard_interpreter.rb - lib/chef/guard_interpreter/default_guard_interpreter.rb - lib/chef/guard_interpreter/resource_guard_interpreter.rb - lib/chef/handler.rb - lib/chef/handler/error_report.rb - lib/chef/handler/json_file.rb - lib/chef/http.rb - lib/chef/http/auth_credentials.rb - lib/chef/http/authenticator.rb - lib/chef/http/basic_client.rb - lib/chef/http/cookie_jar.rb - lib/chef/http/cookie_manager.rb - lib/chef/http/decompressor.rb - lib/chef/http/http_request.rb - lib/chef/http/json_input.rb - lib/chef/http/json_output.rb - lib/chef/http/json_to_model_output.rb - lib/chef/http/remote_request_id.rb - lib/chef/http/simple.rb - lib/chef/http/socketless_chef_zero_client.rb - lib/chef/http/ssl_policies.rb - lib/chef/http/validate_content_length.rb - lib/chef/json_compat.rb - lib/chef/knife.rb - lib/chef/knife/bootstrap.rb - lib/chef/knife/bootstrap/chef_vault_handler.rb - lib/chef/knife/bootstrap/client_builder.rb - lib/chef/knife/bootstrap/templates/README.md - lib/chef/knife/bootstrap/templates/archlinux-gems.erb - lib/chef/knife/bootstrap/templates/chef-aix.erb - lib/chef/knife/bootstrap/templates/chef-full.erb - lib/chef/knife/client_bulk_delete.rb - lib/chef/knife/client_create.rb - lib/chef/knife/client_delete.rb - lib/chef/knife/client_edit.rb - lib/chef/knife/client_list.rb - lib/chef/knife/client_reregister.rb - lib/chef/knife/client_show.rb - lib/chef/knife/configure.rb - lib/chef/knife/configure_client.rb - lib/chef/knife/cookbook_bulk_delete.rb - lib/chef/knife/cookbook_create.rb - lib/chef/knife/cookbook_delete.rb - lib/chef/knife/cookbook_download.rb - lib/chef/knife/cookbook_list.rb - lib/chef/knife/cookbook_metadata.rb - lib/chef/knife/cookbook_metadata_from_file.rb - lib/chef/knife/cookbook_show.rb - lib/chef/knife/cookbook_site_download.rb - lib/chef/knife/cookbook_site_install.rb - lib/chef/knife/cookbook_site_list.rb - lib/chef/knife/cookbook_site_search.rb - lib/chef/knife/cookbook_site_share.rb - lib/chef/knife/cookbook_site_show.rb - lib/chef/knife/cookbook_site_unshare.rb - lib/chef/knife/cookbook_site_vendor.rb - lib/chef/knife/cookbook_test.rb - lib/chef/knife/cookbook_upload.rb - lib/chef/knife/core/bootstrap_context.rb - lib/chef/knife/core/cookbook_scm_repo.rb - lib/chef/knife/core/generic_presenter.rb - lib/chef/knife/core/node_editor.rb - lib/chef/knife/core/node_presenter.rb - lib/chef/knife/core/object_loader.rb - lib/chef/knife/core/status_presenter.rb - lib/chef/knife/core/subcommand_loader.rb - lib/chef/knife/core/text_formatter.rb - lib/chef/knife/core/ui.rb - lib/chef/knife/data_bag_create.rb - lib/chef/knife/data_bag_delete.rb - lib/chef/knife/data_bag_edit.rb - lib/chef/knife/data_bag_from_file.rb - lib/chef/knife/data_bag_list.rb - lib/chef/knife/data_bag_secret_options.rb - lib/chef/knife/data_bag_show.rb - lib/chef/knife/delete.rb - lib/chef/knife/deps.rb - lib/chef/knife/diff.rb - lib/chef/knife/download.rb - lib/chef/knife/edit.rb - lib/chef/knife/environment_compare.rb - lib/chef/knife/environment_create.rb - lib/chef/knife/environment_delete.rb - lib/chef/knife/environment_edit.rb - lib/chef/knife/environment_from_file.rb - lib/chef/knife/environment_list.rb - lib/chef/knife/environment_show.rb - lib/chef/knife/exec.rb - lib/chef/knife/help.rb - lib/chef/knife/help_topics.rb - lib/chef/knife/index_rebuild.rb - lib/chef/knife/list.rb - lib/chef/knife/node_bulk_delete.rb - lib/chef/knife/node_create.rb - lib/chef/knife/node_delete.rb - lib/chef/knife/node_edit.rb - lib/chef/knife/node_environment_set.rb - lib/chef/knife/node_from_file.rb - lib/chef/knife/node_list.rb - lib/chef/knife/node_run_list_add.rb - lib/chef/knife/node_run_list_remove.rb - lib/chef/knife/node_run_list_set.rb - lib/chef/knife/node_show.rb - lib/chef/knife/raw.rb - lib/chef/knife/recipe_list.rb - lib/chef/knife/role_bulk_delete.rb - lib/chef/knife/role_create.rb - lib/chef/knife/role_delete.rb - lib/chef/knife/role_edit.rb - lib/chef/knife/role_env_run_list_add.rb - lib/chef/knife/role_env_run_list_clear.rb - lib/chef/knife/role_env_run_list_remove.rb - lib/chef/knife/role_env_run_list_replace.rb - lib/chef/knife/role_env_run_list_set.rb - lib/chef/knife/role_from_file.rb - lib/chef/knife/role_list.rb - lib/chef/knife/role_run_list_add.rb - lib/chef/knife/role_run_list_clear.rb - lib/chef/knife/role_run_list_remove.rb - lib/chef/knife/role_run_list_replace.rb - lib/chef/knife/role_run_list_set.rb - lib/chef/knife/role_show.rb - lib/chef/knife/search.rb - lib/chef/knife/serve.rb - lib/chef/knife/show.rb - lib/chef/knife/ssh.rb - lib/chef/knife/ssl_check.rb - lib/chef/knife/ssl_fetch.rb - lib/chef/knife/status.rb - lib/chef/knife/tag_create.rb - lib/chef/knife/tag_delete.rb - lib/chef/knife/tag_list.rb - lib/chef/knife/upload.rb - lib/chef/knife/user_create.rb - lib/chef/knife/user_delete.rb - lib/chef/knife/user_edit.rb - lib/chef/knife/user_list.rb - lib/chef/knife/user_reregister.rb - lib/chef/knife/user_show.rb - lib/chef/knife/xargs.rb - lib/chef/local_mode.rb - lib/chef/log.rb - lib/chef/mash.rb - lib/chef/mixin/checksum.rb - lib/chef/mixin/command.rb - lib/chef/mixin/command/unix.rb - lib/chef/mixin/command/windows.rb - lib/chef/mixin/convert_to_class_name.rb - lib/chef/mixin/create_path.rb - lib/chef/mixin/deep_merge.rb - lib/chef/mixin/deprecation.rb - lib/chef/mixin/descendants_tracker.rb - lib/chef/mixin/enforce_ownership_and_permissions.rb - lib/chef/mixin/file_class.rb - lib/chef/mixin/from_file.rb - lib/chef/mixin/get_source_from_package.rb - lib/chef/mixin/homebrew_user.rb - lib/chef/mixin/language.rb - lib/chef/mixin/language_include_attribute.rb - lib/chef/mixin/language_include_recipe.rb - lib/chef/mixin/params_validate.rb - lib/chef/mixin/path_sanity.rb - lib/chef/mixin/powershell_type_coercions.rb - lib/chef/mixin/provides.rb - lib/chef/mixin/recipe_definition_dsl_core.rb - lib/chef/mixin/securable.rb - lib/chef/mixin/shell_out.rb - lib/chef/mixin/template.rb - lib/chef/mixin/which.rb - lib/chef/mixin/why_run.rb - lib/chef/mixin/windows_architecture_helper.rb - lib/chef/mixin/windows_env_helper.rb - lib/chef/mixin/xml_escape.rb - lib/chef/mixins.rb - lib/chef/monkey_patches/net-ssh-multi.rb - lib/chef/monkey_patches/net_http.rb - lib/chef/monologger.rb - lib/chef/nil_argument.rb - lib/chef/node.rb - lib/chef/node/attribute.rb - lib/chef/node/attribute_collections.rb - lib/chef/node/immutable_collections.rb - lib/chef/node_map.rb - lib/chef/null_logger.rb - lib/chef/org.rb - lib/chef/platform.rb - lib/chef/platform/provider_mapping.rb - lib/chef/platform/provider_priority_map.rb - lib/chef/platform/query_helpers.rb - lib/chef/platform/rebooter.rb - lib/chef/platform/resource_priority_map.rb - lib/chef/platform/service_helpers.rb - lib/chef/policy_builder.rb - lib/chef/policy_builder/expand_node_object.rb - lib/chef/policy_builder/policyfile.rb - lib/chef/provider.rb - lib/chef/provider/batch.rb - lib/chef/provider/breakpoint.rb - lib/chef/provider/cookbook_file.rb - lib/chef/provider/cookbook_file/content.rb - lib/chef/provider/cron.rb - lib/chef/provider/cron/aix.rb - lib/chef/provider/cron/solaris.rb - lib/chef/provider/cron/unix.rb - lib/chef/provider/deploy.rb - lib/chef/provider/deploy/revision.rb - lib/chef/provider/deploy/timestamped.rb - lib/chef/provider/directory.rb - lib/chef/provider/dsc_resource.rb - lib/chef/provider/dsc_script.rb - lib/chef/provider/env.rb - lib/chef/provider/env/windows.rb - lib/chef/provider/erl_call.rb - lib/chef/provider/execute.rb - lib/chef/provider/file.rb - lib/chef/provider/file/content.rb - lib/chef/provider/git.rb - lib/chef/provider/group.rb - lib/chef/provider/group/aix.rb - lib/chef/provider/group/dscl.rb - lib/chef/provider/group/gpasswd.rb - lib/chef/provider/group/groupadd.rb - lib/chef/provider/group/groupmod.rb - lib/chef/provider/group/pw.rb - lib/chef/provider/group/suse.rb - lib/chef/provider/group/usermod.rb - lib/chef/provider/group/windows.rb - lib/chef/provider/http_request.rb - lib/chef/provider/ifconfig.rb - lib/chef/provider/ifconfig/aix.rb - lib/chef/provider/ifconfig/debian.rb - lib/chef/provider/ifconfig/redhat.rb - lib/chef/provider/link.rb - lib/chef/provider/log.rb - lib/chef/provider/lwrp_base.rb - lib/chef/provider/mdadm.rb - lib/chef/provider/mount.rb - lib/chef/provider/mount/aix.rb - lib/chef/provider/mount/mount.rb - lib/chef/provider/mount/solaris.rb - lib/chef/provider/mount/windows.rb - lib/chef/provider/ohai.rb - lib/chef/provider/package.rb - lib/chef/provider/package/aix.rb - lib/chef/provider/package/apt.rb - lib/chef/provider/package/dpkg.rb - lib/chef/provider/package/easy_install.rb - lib/chef/provider/package/freebsd/base.rb - lib/chef/provider/package/freebsd/pkg.rb - lib/chef/provider/package/freebsd/pkgng.rb - lib/chef/provider/package/freebsd/port.rb - lib/chef/provider/package/homebrew.rb - lib/chef/provider/package/ips.rb - lib/chef/provider/package/macports.rb - lib/chef/provider/package/openbsd.rb - lib/chef/provider/package/pacman.rb - lib/chef/provider/package/paludis.rb - lib/chef/provider/package/portage.rb - lib/chef/provider/package/rpm.rb - lib/chef/provider/package/rubygems.rb - lib/chef/provider/package/smartos.rb - lib/chef/provider/package/solaris.rb - lib/chef/provider/package/windows.rb - lib/chef/provider/package/windows/msi.rb - lib/chef/provider/package/yum-dump.py - lib/chef/provider/package/yum.rb - lib/chef/provider/package/zypper.rb - lib/chef/provider/powershell_script.rb - lib/chef/provider/reboot.rb - lib/chef/provider/registry_key.rb - lib/chef/provider/remote_directory.rb - lib/chef/provider/remote_file.rb - lib/chef/provider/remote_file/cache_control_data.rb - lib/chef/provider/remote_file/content.rb - lib/chef/provider/remote_file/fetcher.rb - lib/chef/provider/remote_file/ftp.rb - lib/chef/provider/remote_file/http.rb - lib/chef/provider/remote_file/local_file.rb - lib/chef/provider/resource_update.rb - lib/chef/provider/route.rb - lib/chef/provider/ruby_block.rb - lib/chef/provider/script.rb - lib/chef/provider/service.rb - lib/chef/provider/service/aix.rb - lib/chef/provider/service/aixinit.rb - lib/chef/provider/service/arch.rb - lib/chef/provider/service/debian.rb - lib/chef/provider/service/freebsd.rb - lib/chef/provider/service/gentoo.rb - lib/chef/provider/service/init.rb - lib/chef/provider/service/insserv.rb - lib/chef/provider/service/invokercd.rb - lib/chef/provider/service/macosx.rb - lib/chef/provider/service/openbsd.rb - lib/chef/provider/service/redhat.rb - lib/chef/provider/service/simple.rb - lib/chef/provider/service/solaris.rb - lib/chef/provider/service/systemd.rb - lib/chef/provider/service/upstart.rb - lib/chef/provider/service/windows.rb - lib/chef/provider/subversion.rb - lib/chef/provider/template.rb - lib/chef/provider/template/content.rb - lib/chef/provider/template_finder.rb - lib/chef/provider/user.rb - lib/chef/provider/user/aix.rb - lib/chef/provider/user/dscl.rb - lib/chef/provider/user/pw.rb - lib/chef/provider/user/solaris.rb - lib/chef/provider/user/useradd.rb - lib/chef/provider/user/windows.rb - lib/chef/provider/whyrun_safe_ruby_block.rb - lib/chef/provider/windows_script.rb - lib/chef/provider_resolver.rb - lib/chef/providers.rb - lib/chef/recipe.rb - lib/chef/request_id.rb - lib/chef/reserved_names.rb - lib/chef/resource.rb - lib/chef/resource/apt_package.rb - lib/chef/resource/bash.rb - lib/chef/resource/batch.rb - lib/chef/resource/bff_package.rb - lib/chef/resource/breakpoint.rb - lib/chef/resource/chef_gem.rb - lib/chef/resource/conditional.rb - lib/chef/resource/conditional_action_not_nothing.rb - lib/chef/resource/cookbook_file.rb - lib/chef/resource/cron.rb - lib/chef/resource/csh.rb - lib/chef/resource/deploy.rb - lib/chef/resource/deploy_revision.rb - lib/chef/resource/directory.rb - lib/chef/resource/dpkg_package.rb - lib/chef/resource/dsc_resource.rb - lib/chef/resource/dsc_script.rb - lib/chef/resource/easy_install_package.rb - lib/chef/resource/env.rb - lib/chef/resource/erl_call.rb - lib/chef/resource/execute.rb - lib/chef/resource/file.rb - lib/chef/resource/file/verification.rb - lib/chef/resource/freebsd_package.rb - lib/chef/resource/gem_package.rb - lib/chef/resource/git.rb - lib/chef/resource/group.rb - lib/chef/resource/homebrew_package.rb - lib/chef/resource/http_request.rb - lib/chef/resource/ifconfig.rb - lib/chef/resource/ips_package.rb - lib/chef/resource/link.rb - lib/chef/resource/log.rb - lib/chef/resource/lwrp_base.rb - lib/chef/resource/macosx_service.rb - lib/chef/resource/macports_package.rb - lib/chef/resource/mdadm.rb - lib/chef/resource/mount.rb - lib/chef/resource/ohai.rb - lib/chef/resource/openbsd_package.rb - lib/chef/resource/package.rb - lib/chef/resource/pacman_package.rb - lib/chef/resource/paludis_package.rb - lib/chef/resource/perl.rb - lib/chef/resource/portage_package.rb - lib/chef/resource/powershell_script.rb - lib/chef/resource/python.rb - lib/chef/resource/reboot.rb - lib/chef/resource/registry_key.rb - lib/chef/resource/remote_directory.rb - lib/chef/resource/remote_file.rb - lib/chef/resource/resource_notification.rb - lib/chef/resource/route.rb - lib/chef/resource/rpm_package.rb - lib/chef/resource/ruby.rb - lib/chef/resource/ruby_block.rb - lib/chef/resource/scm.rb - lib/chef/resource/script.rb - lib/chef/resource/service.rb - lib/chef/resource/smartos_package.rb - lib/chef/resource/solaris_package.rb - lib/chef/resource/subversion.rb - lib/chef/resource/template.rb - lib/chef/resource/timestamped_deploy.rb - lib/chef/resource/user.rb - lib/chef/resource/whyrun_safe_ruby_block.rb - lib/chef/resource/windows_package.rb - lib/chef/resource/windows_script.rb - lib/chef/resource/windows_service.rb - lib/chef/resource/yum_package.rb - lib/chef/resource_builder.rb - lib/chef/resource_collection.rb - lib/chef/resource_collection/resource_collection_serialization.rb - lib/chef/resource_collection/resource_list.rb - lib/chef/resource_collection/resource_set.rb - lib/chef/resource_collection/stepable_iterator.rb - lib/chef/resource_definition.rb - lib/chef/resource_definition_list.rb - lib/chef/resource_reporter.rb - lib/chef/resource_resolver.rb - lib/chef/resources.rb - lib/chef/rest.rb - lib/chef/role.rb - lib/chef/run_context.rb - lib/chef/run_context/cookbook_compiler.rb - lib/chef/run_list.rb - lib/chef/run_list/run_list_expansion.rb - lib/chef/run_list/run_list_item.rb - lib/chef/run_list/versioned_recipe_list.rb - lib/chef/run_lock.rb - lib/chef/run_status.rb - lib/chef/runner.rb - lib/chef/sandbox.rb - lib/chef/scan_access_control.rb - lib/chef/search/query.rb - lib/chef/server_api.rb - lib/chef/shell.rb - lib/chef/shell/ext.rb - lib/chef/shell/model_wrapper.rb - lib/chef/shell/shell_rest.rb - lib/chef/shell/shell_session.rb - lib/chef/shell_out.rb - lib/chef/tasks/chef_repo.rake - lib/chef/user.rb - lib/chef/util/backup.rb - lib/chef/util/diff.rb - lib/chef/util/dsc/configuration_generator.rb - lib/chef/util/dsc/lcm_output_parser.rb - lib/chef/util/dsc/local_configuration_manager.rb - lib/chef/util/dsc/resource_info.rb - lib/chef/util/dsc/resource_store.rb - lib/chef/util/editor.rb - lib/chef/util/file_edit.rb - lib/chef/util/path_helper.rb - lib/chef/util/powershell/cmdlet.rb - lib/chef/util/powershell/cmdlet_result.rb - lib/chef/util/powershell/ps_credential.rb - lib/chef/util/selinux.rb - lib/chef/util/threaded_job_queue.rb - lib/chef/util/windows.rb - lib/chef/util/windows/net_group.rb - lib/chef/util/windows/net_use.rb - lib/chef/util/windows/net_user.rb - lib/chef/util/windows/volume.rb - lib/chef/version.rb - lib/chef/version/platform.rb - lib/chef/version_class.rb - lib/chef/version_constraint.rb - lib/chef/version_constraint/platform.rb - lib/chef/whitelist.rb - lib/chef/win32/api.rb - lib/chef/win32/api/crypto.rb - lib/chef/win32/api/error.rb - lib/chef/win32/api/file.rb - lib/chef/win32/api/installer.rb - lib/chef/win32/api/memory.rb - lib/chef/win32/api/net.rb - lib/chef/win32/api/process.rb - lib/chef/win32/api/psapi.rb - lib/chef/win32/api/security.rb - lib/chef/win32/api/synchronization.rb - lib/chef/win32/api/system.rb - lib/chef/win32/api/unicode.rb - lib/chef/win32/crypto.rb - lib/chef/win32/error.rb - lib/chef/win32/file.rb - lib/chef/win32/file/info.rb - lib/chef/win32/handle.rb - lib/chef/win32/memory.rb - lib/chef/win32/mutex.rb - lib/chef/win32/process.rb - lib/chef/win32/registry.rb - lib/chef/win32/security.rb - lib/chef/win32/security/ace.rb - lib/chef/win32/security/acl.rb - lib/chef/win32/security/securable_object.rb - lib/chef/win32/security/security_descriptor.rb - lib/chef/win32/security/sid.rb - lib/chef/win32/security/token.rb - lib/chef/win32/unicode.rb - lib/chef/win32/version.rb - lib/chef/workstation_config_loader.rb - spec/data/apt/chef-integration-test-1.0/debian/changelog - spec/data/apt/chef-integration-test-1.0/debian/compat - spec/data/apt/chef-integration-test-1.0/debian/control - spec/data/apt/chef-integration-test-1.0/debian/copyright - spec/data/apt/chef-integration-test-1.0/debian/files - spec/data/apt/chef-integration-test-1.0/debian/rules - spec/data/apt/chef-integration-test-1.0/debian/source/format - spec/data/apt/chef-integration-test-1.1/debian/changelog - spec/data/apt/chef-integration-test-1.1/debian/compat - spec/data/apt/chef-integration-test-1.1/debian/control - spec/data/apt/chef-integration-test-1.1/debian/copyright - spec/data/apt/chef-integration-test-1.1/debian/files - spec/data/apt/chef-integration-test-1.1/debian/rules - spec/data/apt/chef-integration-test-1.1/debian/source/format - spec/data/apt/chef-integration-test_1.0-1_amd64.changes - spec/data/apt/chef-integration-test_1.0-1_amd64.deb - spec/data/apt/chef-integration-test_1.0.orig.tar.gz - spec/data/apt/chef-integration-test_1.1-1_amd64.changes - spec/data/apt/chef-integration-test_1.1-1_amd64.deb - spec/data/apt/chef-integration-test_1.1.orig.tar.gz - spec/data/apt/var/www/apt/conf/distributions - spec/data/apt/var/www/apt/conf/incoming - spec/data/apt/var/www/apt/conf/pulls - spec/data/apt/var/www/apt/db/checksums.db - spec/data/apt/var/www/apt/db/contents.cache.db - spec/data/apt/var/www/apt/db/packages.db - spec/data/apt/var/www/apt/db/references.db - spec/data/apt/var/www/apt/db/release.caches.db - spec/data/apt/var/www/apt/db/version - spec/data/apt/var/www/apt/dists/sid/Release - spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages - spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Packages.gz - spec/data/apt/var/www/apt/dists/sid/main/binary-amd64/Release - spec/data/apt/var/www/apt/dists/sid/main/binary-i386/Packages - spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.0-1_amd64.deb - spec/data/apt/var/www/apt/pool/main/c/chef-integration-test/chef-integration-test_1.1-1_amd64.deb - spec/data/bad-config.rb - spec/data/bootstrap/encrypted_data_bag_secret - spec/data/bootstrap/no_proxy.erb - spec/data/bootstrap/secret.erb - spec/data/bootstrap/test-hints.erb - spec/data/bootstrap/test.erb - spec/data/cb_version_cookbooks/cookbook2/files/test.txt - spec/data/cb_version_cookbooks/cookbook2/templates/test.erb - spec/data/cb_version_cookbooks/tatft/README.rdoc - spec/data/cb_version_cookbooks/tatft/attributes/default.rb - spec/data/cb_version_cookbooks/tatft/definitions/runit_service.rb - spec/data/cb_version_cookbooks/tatft/files/default/giant_blob.tgz - spec/data/cb_version_cookbooks/tatft/libraries/ownage.rb - spec/data/cb_version_cookbooks/tatft/providers/lwp.rb - spec/data/cb_version_cookbooks/tatft/recipes/default.rb - spec/data/cb_version_cookbooks/tatft/resources/lwr.rb - spec/data/cb_version_cookbooks/tatft/templates/default/configuration.erb - spec/data/checksum/random.txt - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-600hhz-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-6m8zdk-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ahd2gq-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-api8ux-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-b0r1m1-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-bfygsi-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-el14l6-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ivrl3y-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-kkbs85-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ory1ux-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-pgsq76-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ra8uim-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t7k1g-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-t8g0sv-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-ufy6g3-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-x2d6j9-0 - spec/data/checksum_cache/chef-file--tmp-chef-rendered-template20100929-10863-xi0l6h-0 - spec/data/config.rb - spec/data/cookbooks/angrybash/metadata.rb - spec/data/cookbooks/angrybash/recipes/default.rb - spec/data/cookbooks/apache2/files/default/apache2_module_conf_generate.pl - spec/data/cookbooks/apache2/metadata.rb - spec/data/cookbooks/apache2/recipes/default.rb - spec/data/cookbooks/borken/metadata.rb - spec/data/cookbooks/borken/recipes/default.rb - spec/data/cookbooks/borken/templates/default/borken.erb - spec/data/cookbooks/chefignore - spec/data/cookbooks/ignorken/files/default/not_me.rb - spec/data/cookbooks/ignorken/metadata.rb - spec/data/cookbooks/ignorken/recipes/default.rb - spec/data/cookbooks/ignorken/recipes/ignoreme.rb - spec/data/cookbooks/ignorken/templates/ubuntu-12.10/not_me.rb - spec/data/cookbooks/java/files/default/java.response - spec/data/cookbooks/java/metadata.rb - spec/data/cookbooks/name-mismatch-versionnumber/README.md - spec/data/cookbooks/name-mismatch-versionnumber/metadata.rb - spec/data/cookbooks/name-mismatch-versionnumber/recipes/default.rb - spec/data/cookbooks/openldap/attributes/default.rb - spec/data/cookbooks/openldap/attributes/smokey.rb - spec/data/cookbooks/openldap/definitions/client.rb - spec/data/cookbooks/openldap/definitions/server.rb - spec/data/cookbooks/openldap/files/default/.dotfile - spec/data/cookbooks/openldap/files/default/.ssh/id_rsa - spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir - spec/data/cookbooks/openldap/files/default/remotedir/not_a_template.erb - spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file1.txt - spec/data/cookbooks/openldap/files/default/remotedir/remote_dir_file2.txt - spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile - spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file1.txt - spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/remote_subdir_file2.txt - spec/data/cookbooks/openldap/files/default/remotedir/subdir_with_no_file_just_a_subsubdir/the_subsubdir/some_file.txt - spec/data/cookbooks/openldap/libraries/openldap.rb - spec/data/cookbooks/openldap/libraries/openldap/version.rb - spec/data/cookbooks/openldap/metadata.rb - spec/data/cookbooks/openldap/recipes/default.rb - spec/data/cookbooks/openldap/recipes/gigantor.rb - spec/data/cookbooks/openldap/recipes/one.rb - spec/data/cookbooks/openldap/recipes/return.rb - spec/data/cookbooks/openldap/templates/default/all_windows_line_endings.erb - spec/data/cookbooks/openldap/templates/default/helper_test.erb - spec/data/cookbooks/openldap/templates/default/helpers_via_partial_test.erb - spec/data/cookbooks/openldap/templates/default/no_windows_line_endings.erb - spec/data/cookbooks/openldap/templates/default/openldap_stuff.conf.erb - spec/data/cookbooks/openldap/templates/default/openldap_variable_stuff.conf.erb - spec/data/cookbooks/openldap/templates/default/some_windows_line_endings.erb - spec/data/cookbooks/openldap/templates/default/test.erb - spec/data/cookbooks/preseed/files/default/preseed-file.seed - spec/data/cookbooks/preseed/files/default/preseed-template.seed - spec/data/cookbooks/preseed/metadata.rb - spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed - spec/data/cookbooks/preseed/templates/default/preseed-template.seed - spec/data/definitions/test.rb - spec/data/environment-config.rb - spec/data/file-providers-method-snapshot-chef-11-4.json - spec/data/fileedit/blank - spec/data/fileedit/hosts - spec/data/gems/chef-integration-test-0.1.0.gem - spec/data/git_bundles/example-repo.gitbundle - spec/data/git_bundles/sinatra-test-app-with-callback-files.gitbundle - spec/data/git_bundles/sinatra-test-app-with-symlinks.gitbundle - spec/data/git_bundles/sinatra-test-app.gitbundle - spec/data/incomplete-metadata-chef-repo/incomplete-metadata/README.md - spec/data/incomplete-metadata-chef-repo/incomplete-metadata/metadata.rb - spec/data/incomplete-metadata-chef-repo/incomplete-metadata/recipes/default.rb - spec/data/invalid-metadata-chef-repo/invalid-metadata/README.md - spec/data/invalid-metadata-chef-repo/invalid-metadata/metadata.rb - spec/data/invalid-metadata-chef-repo/invalid-metadata/recipes/default.rb - spec/data/kitchen/chefignore - spec/data/kitchen/openldap/attributes/default.rb - spec/data/kitchen/openldap/attributes/robinson.rb - spec/data/kitchen/openldap/definitions/client.rb - spec/data/kitchen/openldap/definitions/drewbarrymore.rb - spec/data/kitchen/openldap/recipes/gigantor.rb - spec/data/kitchen/openldap/recipes/ignoreme.rb - spec/data/kitchen/openldap/recipes/woot.rb - spec/data/knife-home/.chef/plugins/knife/example_home_subcommand.rb - spec/data/knife-site-subcommands/plugins/knife/example_subcommand.rb - spec/data/knife_subcommand/test_explicit_category.rb - spec/data/knife_subcommand/test_name_mapping.rb - spec/data/knife_subcommand/test_yourself.rb - spec/data/lwrp/providers/buck_passer.rb - spec/data/lwrp/providers/buck_passer_2.rb - spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb - spec/data/lwrp/providers/inline_compiler.rb - spec/data/lwrp/providers/monkey_name_printer.rb - spec/data/lwrp/providers/paint_drying_watcher.rb - spec/data/lwrp/providers/thumb_twiddler.rb - spec/data/lwrp/resources/bar.rb - spec/data/lwrp/resources/foo.rb - spec/data/lwrp/resources_with_default_attributes/nodeattr.rb - spec/data/lwrp_const_scoping/resources/conflict.rb - spec/data/lwrp_override/providers/buck_passer.rb - spec/data/lwrp_override/resources/foo.rb - spec/data/mac_users/10.7-8.plist.xml - spec/data/mac_users/10.7-8.shadow.xml - spec/data/mac_users/10.7.plist.xml - spec/data/mac_users/10.7.shadow.xml - spec/data/mac_users/10.8.plist.xml - spec/data/mac_users/10.8.shadow.xml - spec/data/mac_users/10.9.plist.xml - spec/data/mac_users/10.9.shadow.xml - spec/data/metadata/quick_start/metadata.rb - spec/data/nested.json - spec/data/nodes/default.rb - spec/data/nodes/test.example.com.rb - spec/data/nodes/test.rb - spec/data/null_config.rb - spec/data/object_loader/environments/test.json - spec/data/object_loader/environments/test.rb - spec/data/object_loader/environments/test_json_class.json - spec/data/object_loader/nodes/test.json - spec/data/object_loader/nodes/test.rb - spec/data/object_loader/nodes/test_json_class.json - spec/data/object_loader/roles/test.json - spec/data/object_loader/roles/test.rb - spec/data/object_loader/roles/test_json_class.json - spec/data/old_home_dir/my-dot-emacs - spec/data/old_home_dir/my-dot-vim - spec/data/partial_one.erb - spec/data/recipes.tgz - spec/data/recipes/test.rb - spec/data/remote_directory_data/remote_dir_file.txt - spec/data/remote_directory_data/remote_subdirectory/remote_subdir_file.txt - spec/data/remote_file/nyan_cat.png - spec/data/remote_file/nyan_cat.png.gz - spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb - spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb - spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb - spec/data/run_context/cookbooks/circular-dep1/metadata.rb - spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb - spec/data/run_context/cookbooks/circular-dep1/recipes/default.rb - spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb - spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb - spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb - spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb - spec/data/run_context/cookbooks/circular-dep2/metadata.rb - spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb - spec/data/run_context/cookbooks/circular-dep2/recipes/default.rb - spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb - spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb - spec/data/run_context/cookbooks/dependency1/attributes/default.rb - spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb - spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb - spec/data/run_context/cookbooks/dependency1/libraries/lib.rb - spec/data/run_context/cookbooks/dependency1/providers/provider.rb - spec/data/run_context/cookbooks/dependency1/recipes/default.rb - spec/data/run_context/cookbooks/dependency1/resources/resource.rb - spec/data/run_context/cookbooks/dependency2/attributes/default.rb - spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb - spec/data/run_context/cookbooks/dependency2/libraries/lib.rb - spec/data/run_context/cookbooks/dependency2/providers/provider.rb - spec/data/run_context/cookbooks/dependency2/recipes/default.rb - spec/data/run_context/cookbooks/dependency2/resources/resource.rb - spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb - spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb - spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb - spec/data/run_context/cookbooks/no-default-attr/recipes/default.rb - spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb - spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb - spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rb - spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb - spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb - spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb - spec/data/run_context/cookbooks/test-with-circular-deps/recipes/default.rb - spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb - spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb - spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb - spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb - spec/data/run_context/cookbooks/test-with-deps/metadata.rb - spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb - spec/data/run_context/cookbooks/test-with-deps/recipes/default.rb - spec/data/run_context/cookbooks/test-with-deps/recipes/server.rb - spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb - spec/data/run_context/cookbooks/test/attributes/default.rb - spec/data/run_context/cookbooks/test/attributes/george.rb - spec/data/run_context/cookbooks/test/definitions/new_animals.rb - spec/data/run_context/cookbooks/test/definitions/new_cat.rb - spec/data/run_context/cookbooks/test/definitions/test_res.rb - spec/data/run_context/cookbooks/test/providers/provider.rb - spec/data/run_context/cookbooks/test/recipes/default.rb - spec/data/run_context/cookbooks/test/recipes/one.rb - spec/data/run_context/cookbooks/test/recipes/two.rb - spec/data/run_context/cookbooks/test/resources/resource.rb - spec/data/run_context/nodes/run_context.rb - spec/data/search_queries_to_transform.txt - spec/data/shef-config.rb - spec/data/ssl/5e707473.0 - spec/data/ssl/chef-rspec.cert - spec/data/ssl/chef-rspec.key - spec/data/ssl/key.pem - spec/data/ssl/private_key.pem - spec/data/ssl/private_key_with_whitespace.pem - spec/data/standalone_cookbook/Gemfile - spec/data/standalone_cookbook/chefignore - spec/data/standalone_cookbook/recipes/default.rb - spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rb - spec/data/templates/seattle.txt - spec/data/trusted_certs/example.crt - spec/data/trusted_certs/intermediate.pem - spec/data/trusted_certs/opscode.pem - spec/data/trusted_certs/root.pem - spec/functional/application_spec.rb - spec/functional/assets/PkgA.1.0.0.0.bff - spec/functional/assets/PkgA.2.0.0.0.bff - spec/functional/assets/chefinittest - spec/functional/assets/dummy-1-0.aix6.1.noarch.rpm - spec/functional/assets/dummy-2-0.aix6.1.noarch.rpm - spec/functional/assets/mytest-1.0-1.noarch.rpm - spec/functional/assets/mytest-2.0-1.noarch.rpm - spec/functional/assets/testchefsubsys - spec/functional/audit/rspec_formatter_spec.rb - spec/functional/audit/runner_spec.rb - spec/functional/dsl/reboot_pending_spec.rb - spec/functional/dsl/registry_helper_spec.rb - spec/functional/event_loggers/windows_eventlog_spec.rb - spec/functional/file_content_management/deploy_strategies_spec.rb - spec/functional/http/simple_spec.rb - spec/functional/knife/configure_spec.rb - spec/functional/knife/cookbook_delete_spec.rb - spec/functional/knife/exec_spec.rb - spec/functional/knife/smoke_test.rb - spec/functional/knife/ssh_spec.rb - spec/functional/mixin/shell_out_spec.rb - spec/functional/notifications_spec.rb - spec/functional/provider/remote_file/cache_control_data_spec.rb - spec/functional/provider/whyrun_safe_ruby_block_spec.rb - spec/functional/rebooter_spec.rb - spec/functional/resource/aix_service_spec.rb - spec/functional/resource/aixinit_service_spec.rb - spec/functional/resource/base.rb - spec/functional/resource/bash_spec.rb - spec/functional/resource/batch_spec.rb - spec/functional/resource/bff_spec.rb - spec/functional/resource/cookbook_file_spec.rb - spec/functional/resource/cron_spec.rb - spec/functional/resource/deploy_revision_spec.rb - spec/functional/resource/directory_spec.rb - spec/functional/resource/dsc_resource_spec.rb - spec/functional/resource/dsc_script_spec.rb - spec/functional/resource/env_spec.rb - spec/functional/resource/execute_spec.rb - spec/functional/resource/file_spec.rb - spec/functional/resource/git_spec.rb - spec/functional/resource/group_spec.rb - spec/functional/resource/ifconfig_spec.rb - spec/functional/resource/link_spec.rb - spec/functional/resource/mount_spec.rb - spec/functional/resource/ohai_spec.rb - spec/functional/resource/package_spec.rb - spec/functional/resource/powershell_spec.rb - spec/functional/resource/reboot_spec.rb - spec/functional/resource/registry_spec.rb - spec/functional/resource/remote_directory_spec.rb - spec/functional/resource/remote_file_spec.rb - spec/functional/resource/rpm_spec.rb - spec/functional/resource/template_spec.rb - spec/functional/resource/user/dscl_spec.rb - spec/functional/resource/user/useradd_spec.rb - spec/functional/resource/windows_service_spec.rb - spec/functional/rest_spec.rb - spec/functional/run_lock_spec.rb - spec/functional/shell_spec.rb - spec/functional/tiny_server_spec.rb - spec/functional/util/path_helper_spec.rb - spec/functional/util/powershell/cmdlet_spec.rb - spec/functional/version_spec.rb - spec/functional/win32/crypto_spec.rb - spec/functional/win32/registry_helper_spec.rb - spec/functional/win32/security_spec.rb - spec/functional/win32/service_manager_spec.rb - spec/functional/win32/versions_spec.rb - spec/integration/client/client_spec.rb - spec/integration/client/ipv6_spec.rb - spec/integration/knife/chef_fs_data_store_spec.rb - spec/integration/knife/chef_repo_path_spec.rb - spec/integration/knife/chef_repository_file_system_spec.rb - spec/integration/knife/chefignore_spec.rb - spec/integration/knife/common_options_spec.rb - spec/integration/knife/cookbook_api_ipv6_spec.rb - spec/integration/knife/delete_spec.rb - spec/integration/knife/deps_spec.rb - spec/integration/knife/diff_spec.rb - spec/integration/knife/download_spec.rb - spec/integration/knife/list_spec.rb - spec/integration/knife/raw_spec.rb - spec/integration/knife/redirection_spec.rb - spec/integration/knife/serve_spec.rb - spec/integration/knife/show_spec.rb - spec/integration/knife/upload_spec.rb - spec/integration/recipes/lwrp_inline_resources_spec.rb - spec/integration/solo/solo_spec.rb - spec/rcov.opts - spec/scripts/ssl-serve.rb - spec/spec_helper.rb - spec/stress/win32/file_spec.rb - spec/stress/win32/memory_spec.rb - spec/stress/win32/security_spec.rb - spec/support/chef_helpers.rb - spec/support/lib/chef/provider/easy.rb - spec/support/lib/chef/provider/snakeoil.rb - spec/support/lib/chef/resource/cat.rb - spec/support/lib/chef/resource/one_two_three_four.rb - spec/support/lib/chef/resource/with_state.rb - spec/support/lib/chef/resource/zen_follower.rb - spec/support/lib/chef/resource/zen_master.rb - spec/support/lib/library_load_order.rb - spec/support/matchers/leak.rb - spec/support/mock/constant.rb - spec/support/mock/platform.rb - spec/support/pedant/Gemfile - spec/support/pedant/pedant_config.rb - spec/support/pedant/run_pedant.rb - spec/support/pedant/stickywicket.pem - spec/support/platform_helpers.rb - spec/support/platforms/prof/gc.rb - spec/support/platforms/prof/win32.rb - spec/support/platforms/win32/spec_service.rb - spec/support/shared/context/config.rb - spec/support/shared/functional/diff_disabled.rb - spec/support/shared/functional/directory_resource.rb - spec/support/shared/functional/file_resource.rb - spec/support/shared/functional/http.rb - spec/support/shared/functional/knife.rb - spec/support/shared/functional/securable_resource.rb - spec/support/shared/functional/securable_resource_with_reporting.rb - spec/support/shared/functional/win32_service.rb - spec/support/shared/functional/windows_script.rb - spec/support/shared/integration/app_server_support.rb - spec/support/shared/integration/integration_helper.rb - spec/support/shared/integration/knife_support.rb - spec/support/shared/matchers/exit_with_code.rb - spec/support/shared/matchers/match_environment_variable.rb - spec/support/shared/shared_examples.rb - spec/support/shared/unit/api_error_inspector.rb - spec/support/shared/unit/execute_resource.rb - spec/support/shared/unit/file_system_support.rb - spec/support/shared/unit/platform_introspector.rb - spec/support/shared/unit/provider/file.rb - spec/support/shared/unit/provider/useradd_based_user_provider.rb - spec/support/shared/unit/resource/static_provider_resolution.rb - spec/support/shared/unit/script_resource.rb - spec/support/shared/unit/windows_script_resource.rb - spec/tiny_server.rb - spec/unit/api_client/registration_spec.rb - spec/unit/api_client_spec.rb - spec/unit/application/agent_spec.rb - spec/unit/application/apply_spec.rb - spec/unit/application/client_spec.rb - spec/unit/application/knife_spec.rb - spec/unit/application/server_spec.rb - spec/unit/application/solo_spec.rb - spec/unit/application_spec.rb - spec/unit/audit/audit_event_proxy_spec.rb - spec/unit/audit/audit_reporter_spec.rb - spec/unit/audit/control_group_data_spec.rb - spec/unit/audit/rspec_formatter_spec.rb - spec/unit/audit/runner_spec.rb - spec/unit/chef_class_spec.rb - spec/unit/chef_fs/config_spec.rb - spec/unit/chef_fs/data_handler/group_handler_spec.rb - spec/unit/chef_fs/diff_spec.rb - spec/unit/chef_fs/file_pattern_spec.rb - spec/unit/chef_fs/file_system/operation_failed_error_spec.rb - spec/unit/chef_fs/file_system_spec.rb - spec/unit/chef_fs/parallelizer.rb - spec/unit/chef_spec.rb - spec/unit/client_spec.rb - spec/unit/config_fetcher_spec.rb - spec/unit/config_spec.rb - spec/unit/cookbook/chefignore_spec.rb - spec/unit/cookbook/cookbook_version_loader_spec.rb - spec/unit/cookbook/file_vendor_spec.rb - spec/unit/cookbook/metadata_spec.rb - spec/unit/cookbook/synchronizer_spec.rb - spec/unit/cookbook/syntax_check_spec.rb - spec/unit/cookbook_loader_spec.rb - spec/unit/cookbook_manifest_spec.rb - spec/unit/cookbook_site_streaming_uploader_spec.rb - spec/unit/cookbook_spec.rb - spec/unit/cookbook_uploader_spec.rb - spec/unit/cookbook_version_file_specificity_spec.rb - spec/unit/cookbook_version_spec.rb - spec/unit/daemon_spec.rb - spec/unit/data_bag_item_spec.rb - spec/unit/data_bag_spec.rb - spec/unit/deprecation_spec.rb - spec/unit/digester_spec.rb - spec/unit/dsl/audit_spec.rb - spec/unit/dsl/data_query_spec.rb - spec/unit/dsl/platform_introspection_spec.rb - spec/unit/dsl/reboot_pending_spec.rb - spec/unit/dsl/recipe_spec.rb - spec/unit/dsl/regsitry_helper_spec.rb - spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb - spec/unit/encrypted_data_bag_item_spec.rb - spec/unit/environment_spec.rb - spec/unit/exceptions_spec.rb - spec/unit/file_access_control_spec.rb - spec/unit/file_cache_spec.rb - spec/unit/file_content_management/deploy/cp_spec.rb - spec/unit/file_content_management/deploy/mv_unix_spec.rb - spec/unit/file_content_management/deploy/mv_windows_spec.rb - spec/unit/formatters/base_spec.rb - spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb - spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb - spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb - spec/unit/formatters/error_inspectors/node_load_error_inspector_spec.rb - spec/unit/formatters/error_inspectors/registration_error_inspector_spec.rb - spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb - spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb - spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb - spec/unit/guard_interpreter_spec.rb - spec/unit/handler/json_file_spec.rb - spec/unit/handler_spec.rb - spec/unit/http/basic_client_spec.rb - spec/unit/http/http_request_spec.rb - spec/unit/http/json_input_spec.rb - spec/unit/http/simple_spec.rb - spec/unit/http/socketless_chef_zero_client_spec.rb - spec/unit/http/ssl_policies_spec.rb - spec/unit/http/validate_content_length_spec.rb - spec/unit/http_spec.rb - spec/unit/json_compat_spec.rb - spec/unit/knife/bootstrap/chef_vault_handler_spec.rb - spec/unit/knife/bootstrap/client_builder_spec.rb - spec/unit/knife/bootstrap_spec.rb - spec/unit/knife/client_bulk_delete_spec.rb - spec/unit/knife/client_create_spec.rb - spec/unit/knife/client_delete_spec.rb - spec/unit/knife/client_edit_spec.rb - spec/unit/knife/client_list_spec.rb - spec/unit/knife/client_reregister_spec.rb - spec/unit/knife/client_show_spec.rb - spec/unit/knife/configure_client_spec.rb - spec/unit/knife/configure_spec.rb - spec/unit/knife/cookbook_bulk_delete_spec.rb - spec/unit/knife/cookbook_create_spec.rb - spec/unit/knife/cookbook_delete_spec.rb - spec/unit/knife/cookbook_download_spec.rb - spec/unit/knife/cookbook_list_spec.rb - spec/unit/knife/cookbook_metadata_from_file_spec.rb - spec/unit/knife/cookbook_metadata_spec.rb - spec/unit/knife/cookbook_show_spec.rb - spec/unit/knife/cookbook_site_download_spec.rb - spec/unit/knife/cookbook_site_install_spec.rb - spec/unit/knife/cookbook_site_share_spec.rb - spec/unit/knife/cookbook_site_unshare_spec.rb - spec/unit/knife/cookbook_test_spec.rb - spec/unit/knife/cookbook_upload_spec.rb - spec/unit/knife/core/bootstrap_context_spec.rb - spec/unit/knife/core/cookbook_scm_repo_spec.rb - spec/unit/knife/core/object_loader_spec.rb - spec/unit/knife/core/subcommand_loader_spec.rb - spec/unit/knife/core/ui_spec.rb - spec/unit/knife/data_bag_create_spec.rb - spec/unit/knife/data_bag_edit_spec.rb - spec/unit/knife/data_bag_from_file_spec.rb - spec/unit/knife/data_bag_secret_options_spec.rb - spec/unit/knife/data_bag_show_spec.rb - spec/unit/knife/environment_compare_spec.rb - spec/unit/knife/environment_create_spec.rb - spec/unit/knife/environment_delete_spec.rb - spec/unit/knife/environment_edit_spec.rb - spec/unit/knife/environment_from_file_spec.rb - spec/unit/knife/environment_list_spec.rb - spec/unit/knife/environment_show_spec.rb - spec/unit/knife/index_rebuild_spec.rb - spec/unit/knife/knife_help.rb - spec/unit/knife/node_bulk_delete_spec.rb - spec/unit/knife/node_delete_spec.rb - spec/unit/knife/node_edit_spec.rb - spec/unit/knife/node_environment_set_spec.rb - spec/unit/knife/node_from_file_spec.rb - spec/unit/knife/node_list_spec.rb - spec/unit/knife/node_run_list_add_spec.rb - spec/unit/knife/node_run_list_remove_spec.rb - spec/unit/knife/node_run_list_set_spec.rb - spec/unit/knife/node_show_spec.rb - spec/unit/knife/raw_spec.rb - spec/unit/knife/role_bulk_delete_spec.rb - spec/unit/knife/role_create_spec.rb - spec/unit/knife/role_delete_spec.rb - spec/unit/knife/role_edit_spec.rb - spec/unit/knife/role_env_run_list_add_spec.rb - spec/unit/knife/role_env_run_list_clear_spec.rb - spec/unit/knife/role_env_run_list_remove_spec.rb - spec/unit/knife/role_env_run_list_replace_spec.rb - spec/unit/knife/role_env_run_list_set_spec.rb - spec/unit/knife/role_from_file_spec.rb - spec/unit/knife/role_list_spec.rb - spec/unit/knife/role_run_list_add_spec.rb - spec/unit/knife/role_run_list_clear_spec.rb - spec/unit/knife/role_run_list_remove_spec.rb - spec/unit/knife/role_run_list_replace_spec.rb - spec/unit/knife/role_run_list_set_spec.rb - spec/unit/knife/role_show_spec.rb - spec/unit/knife/ssh_spec.rb - spec/unit/knife/ssl_check_spec.rb - spec/unit/knife/ssl_fetch_spec.rb - spec/unit/knife/status_spec.rb - spec/unit/knife/tag_create_spec.rb - spec/unit/knife/tag_delete_spec.rb - spec/unit/knife/tag_list_spec.rb - spec/unit/knife/user_create_spec.rb - spec/unit/knife/user_delete_spec.rb - spec/unit/knife/user_edit_spec.rb - spec/unit/knife/user_list_spec.rb - spec/unit/knife/user_reregister_spec.rb - spec/unit/knife/user_show_spec.rb - spec/unit/knife_spec.rb - spec/unit/log_spec.rb - spec/unit/lwrp_spec.rb - spec/unit/mash_spec.rb - spec/unit/mixin/checksum_spec.rb - spec/unit/mixin/command_spec.rb - spec/unit/mixin/convert_to_class_name_spec.rb - spec/unit/mixin/deep_merge_spec.rb - spec/unit/mixin/deprecation_spec.rb - spec/unit/mixin/enforce_ownership_and_permissions_spec.rb - spec/unit/mixin/homebrew_user_spec.rb - spec/unit/mixin/params_validate_spec.rb - spec/unit/mixin/path_sanity_spec.rb - spec/unit/mixin/powershell_type_coercions_spec.rb - spec/unit/mixin/securable_spec.rb - spec/unit/mixin/shell_out_spec.rb - spec/unit/mixin/template_spec.rb - spec/unit/mixin/windows_architecture_helper_spec.rb - spec/unit/mixin/xml_escape_spec.rb - spec/unit/monkey_patches/uri_spec.rb - spec/unit/monologger_spec.rb - spec/unit/node/attribute_spec.rb - spec/unit/node/immutable_collections_spec.rb - spec/unit/node_map_spec.rb - spec/unit/node_spec.rb - spec/unit/org_spec.rb - spec/unit/platform/query_helpers_spec.rb - spec/unit/platform_spec.rb - spec/unit/policy_builder/expand_node_object_spec.rb - spec/unit/policy_builder/policyfile_spec.rb - spec/unit/policy_builder_spec.rb - spec/unit/provider/breakpoint_spec.rb - spec/unit/provider/cookbook_file/content_spec.rb - spec/unit/provider/cookbook_file_spec.rb - spec/unit/provider/cron/unix_spec.rb - spec/unit/provider/cron_spec.rb - spec/unit/provider/deploy/revision_spec.rb - spec/unit/provider/deploy/timestamped_spec.rb - spec/unit/provider/deploy_spec.rb - spec/unit/provider/directory_spec.rb - spec/unit/provider/dsc_resource_spec.rb - spec/unit/provider/dsc_script_spec.rb - spec/unit/provider/env/windows_spec.rb - spec/unit/provider/env_spec.rb - spec/unit/provider/erl_call_spec.rb - spec/unit/provider/execute_spec.rb - spec/unit/provider/file/content_spec.rb - spec/unit/provider/file_spec.rb - spec/unit/provider/git_spec.rb - spec/unit/provider/group/dscl_spec.rb - spec/unit/provider/group/gpasswd_spec.rb - spec/unit/provider/group/groupadd_spec.rb - spec/unit/provider/group/groupmod_spec.rb - spec/unit/provider/group/pw_spec.rb - spec/unit/provider/group/usermod_spec.rb - spec/unit/provider/group/windows_spec.rb - spec/unit/provider/group_spec.rb - spec/unit/provider/http_request_spec.rb - spec/unit/provider/ifconfig/aix_spec.rb - spec/unit/provider/ifconfig/debian_spec.rb - spec/unit/provider/ifconfig/redhat_spec.rb - spec/unit/provider/ifconfig_spec.rb - spec/unit/provider/link_spec.rb - spec/unit/provider/log_spec.rb - spec/unit/provider/mdadm_spec.rb - spec/unit/provider/mount/aix_spec.rb - spec/unit/provider/mount/mount_spec.rb - spec/unit/provider/mount/solaris_spec.rb - spec/unit/provider/mount/windows_spec.rb - spec/unit/provider/mount_spec.rb - spec/unit/provider/ohai_spec.rb - spec/unit/provider/package/aix_spec.rb - spec/unit/provider/package/apt_spec.rb - spec/unit/provider/package/dpkg_spec.rb - spec/unit/provider/package/easy_install_spec.rb - spec/unit/provider/package/freebsd/pkg_spec.rb - spec/unit/provider/package/freebsd/pkgng_spec.rb - spec/unit/provider/package/freebsd/port_spec.rb - spec/unit/provider/package/homebrew_spec.rb - spec/unit/provider/package/ips_spec.rb - spec/unit/provider/package/macports_spec.rb - spec/unit/provider/package/openbsd_spec.rb - spec/unit/provider/package/pacman_spec.rb - spec/unit/provider/package/paludis_spec.rb - spec/unit/provider/package/portage_spec.rb - spec/unit/provider/package/rpm_spec.rb - spec/unit/provider/package/rubygems_spec.rb - spec/unit/provider/package/smartos_spec.rb - spec/unit/provider/package/solaris_spec.rb - spec/unit/provider/package/windows/msi_spec.rb - spec/unit/provider/package/windows_spec.rb - spec/unit/provider/package/yum_spec.rb - spec/unit/provider/package/zypper_spec.rb - spec/unit/provider/package_spec.rb - spec/unit/provider/package_spec.rbe - spec/unit/provider/powershell_spec.rb - spec/unit/provider/registry_key_spec.rb - spec/unit/provider/remote_directory_spec.rb - spec/unit/provider/remote_file/cache_control_data_spec.rb - spec/unit/provider/remote_file/content_spec.rb - spec/unit/provider/remote_file/fetcher_spec.rb - spec/unit/provider/remote_file/ftp_spec.rb - spec/unit/provider/remote_file/http_spec.rb - spec/unit/provider/remote_file/local_file_spec.rb - spec/unit/provider/remote_file_spec.rb - spec/unit/provider/route_spec.rb - spec/unit/provider/ruby_block_spec.rb - spec/unit/provider/script_spec.rb - spec/unit/provider/service/aix_service_spec.rb - spec/unit/provider/service/aixinit_service_spec.rb - spec/unit/provider/service/arch_service_spec.rb - spec/unit/provider/service/debian_service_spec.rb - spec/unit/provider/service/freebsd_service_spec.rb - spec/unit/provider/service/gentoo_service_spec.rb - spec/unit/provider/service/init_service_spec.rb - spec/unit/provider/service/insserv_service_spec.rb - spec/unit/provider/service/invokercd_service_spec.rb - spec/unit/provider/service/macosx_spec.rb - spec/unit/provider/service/openbsd_service_spec.rb - spec/unit/provider/service/redhat_spec.rb - spec/unit/provider/service/simple_service_spec.rb - spec/unit/provider/service/solaris_smf_service_spec.rb - spec/unit/provider/service/systemd_service_spec.rb - spec/unit/provider/service/upstart_service_spec.rb - spec/unit/provider/service/windows_spec.rb - spec/unit/provider/service_spec.rb - spec/unit/provider/subversion_spec.rb - spec/unit/provider/template/content_spec.rb - spec/unit/provider/template_spec.rb - spec/unit/provider/user/dscl_spec.rb - spec/unit/provider/user/pw_spec.rb - spec/unit/provider/user/solaris_spec.rb - spec/unit/provider/user/useradd_spec.rb - spec/unit/provider/user/windows_spec.rb - spec/unit/provider/user_spec.rb - spec/unit/provider/whyrun_safe_ruby_block_spec.rb - spec/unit/provider_resolver_spec.rb - spec/unit/provider_spec.rb - spec/unit/pure_application_spec.rb - spec/unit/recipe_spec.rb - spec/unit/registry_helper_spec.rb - spec/unit/resource/apt_package_spec.rb - spec/unit/resource/bash_spec.rb - spec/unit/resource/batch_spec.rb - spec/unit/resource/breakpoint_spec.rb - spec/unit/resource/chef_gem_spec.rb - spec/unit/resource/conditional_action_not_nothing_spec.rb - spec/unit/resource/conditional_spec.rb - spec/unit/resource/cookbook_file_spec.rb - spec/unit/resource/cron_spec.rb - spec/unit/resource/csh_spec.rb - spec/unit/resource/deploy_revision_spec.rb - spec/unit/resource/deploy_spec.rb - spec/unit/resource/directory_spec.rb - spec/unit/resource/dpkg_package_spec.rb - spec/unit/resource/dsc_resource_spec.rb - spec/unit/resource/dsc_script_spec.rb - spec/unit/resource/easy_install_package_spec.rb - spec/unit/resource/env_spec.rb - spec/unit/resource/erl_call_spec.rb - spec/unit/resource/execute_spec.rb - spec/unit/resource/file/verification_spec.rb - spec/unit/resource/file_spec.rb - spec/unit/resource/freebsd_package_spec.rb - spec/unit/resource/gem_package_spec.rb - spec/unit/resource/git_spec.rb - spec/unit/resource/group_spec.rb - spec/unit/resource/homebrew_package_spec.rb - spec/unit/resource/http_request_spec.rb - spec/unit/resource/ifconfig_spec.rb - spec/unit/resource/ips_package_spec.rb - spec/unit/resource/link_spec.rb - spec/unit/resource/log_spec.rb - spec/unit/resource/macports_package_spec.rb - spec/unit/resource/mdadm_spec.rb - spec/unit/resource/mount_spec.rb - spec/unit/resource/ohai_spec.rb - spec/unit/resource/openbsd_package_spec.rb - spec/unit/resource/package_spec.rb - spec/unit/resource/pacman_package_spec.rb - spec/unit/resource/perl_spec.rb - spec/unit/resource/portage_package_spec.rb - spec/unit/resource/powershell_spec.rb - spec/unit/resource/python_spec.rb - spec/unit/resource/registry_key_spec.rb - spec/unit/resource/remote_directory_spec.rb - spec/unit/resource/remote_file_spec.rb - spec/unit/resource/resource_notification_spec.rb - spec/unit/resource/route_spec.rb - spec/unit/resource/rpm_package_spec.rb - spec/unit/resource/ruby_block_spec.rb - spec/unit/resource/ruby_spec.rb - spec/unit/resource/scm_spec.rb - spec/unit/resource/script_spec.rb - spec/unit/resource/service_spec.rb - spec/unit/resource/smartos_package_spec.rb - spec/unit/resource/solaris_package_spec.rb - spec/unit/resource/subversion_spec.rb - spec/unit/resource/template_spec.rb - spec/unit/resource/timestamped_deploy_spec.rb - spec/unit/resource/user_spec.rb - spec/unit/resource/windows_package_spec.rb - spec/unit/resource/windows_service_spec.rb - spec/unit/resource/yum_package_spec.rb - spec/unit/resource_builder_spec.rb - spec/unit/resource_collection/resource_list_spec.rb - spec/unit/resource_collection/resource_set_spec.rb - spec/unit/resource_collection/stepable_iterator_spec.rb - spec/unit/resource_collection_spec.rb - spec/unit/resource_definition_spec.rb - spec/unit/resource_reporter_spec.rb - spec/unit/resource_spec.rb - spec/unit/rest/auth_credentials_spec.rb - spec/unit/rest_spec.rb - spec/unit/role_spec.rb - spec/unit/run_context/cookbook_compiler_spec.rb - spec/unit/run_context_spec.rb - spec/unit/run_list/run_list_expansion_spec.rb - spec/unit/run_list/run_list_item_spec.rb - spec/unit/run_list/versioned_recipe_list_spec.rb - spec/unit/run_list_spec.rb - spec/unit/run_lock_spec.rb - spec/unit/run_status_spec.rb - spec/unit/runner_spec.rb - spec/unit/scan_access_control_spec.rb - spec/unit/search/query_spec.rb - spec/unit/shell/model_wrapper_spec.rb - spec/unit/shell/shell_ext_spec.rb - spec/unit/shell/shell_session_spec.rb - spec/unit/shell_out_spec.rb - spec/unit/shell_spec.rb - spec/unit/user_spec.rb - spec/unit/util/backup_spec.rb - spec/unit/util/diff_spec.rb - spec/unit/util/dsc/configuration_generator_spec.rb - spec/unit/util/dsc/lcm_output_parser_spec.rb - spec/unit/util/dsc/local_configuration_manager_spec.rb - spec/unit/util/dsc/resource_store.rb - spec/unit/util/editor_spec.rb - spec/unit/util/file_edit_spec.rb - spec/unit/util/path_helper_spec.rb - spec/unit/util/powershell/cmdlet_spec.rb - spec/unit/util/powershell/ps_credential_spec.rb - spec/unit/util/selinux_spec.rb - spec/unit/util/threaded_job_queue_spec.rb - spec/unit/version/platform_spec.rb - spec/unit/version_class_spec.rb - spec/unit/version_constraint/platform_spec.rb - spec/unit/version_constraint_spec.rb - spec/unit/windows_service_spec.rb - spec/unit/workstation_config_loader_spec.rb - tasks/rspec.rb homepage: http://www.getchef.com licenses: - Apache-2.0 metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.0.0 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.4 signing_key: specification_version: 4 summary: A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure. test_files: [] chef-12.3.0/tasks/0000755000004100000410000000000012520074675013667 5ustar www-datawww-datachef-12.3.0/tasks/rspec.rb0000644000004100000410000000457112520074675015337 0ustar www-datawww-data# # Author:: Adam Jacob () # Author:: Daniel DeLeo () # Copyright:: Copyright (c) 2008, 2010 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' require 'rake' CHEF_ROOT = File.join(File.dirname(__FILE__), "..") begin require 'rspec/core/rake_task' task :default => :spec desc "Run standard specs (minus long running specs)" RSpec::Core::RakeTask.new(:spec) do |t| # right now this just limits to functional + unit, but could also remove # individual tests marked long-running t.pattern = FileList['spec/{functional,unit}/**/*_spec.rb'] end namespace :spec do desc "Run all specs in spec directory with RCov" RSpec::Core::RakeTask.new(:rcov) do |t| t.pattern = FileList['spec/**/*_spec.rb'] t.rcov = true t.rcov_opts = lambda do IO.readlines("#{CHEF_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten end end desc "Run all specs in spec directory" RSpec::Core::RakeTask.new(:all) do |t| t.pattern = FileList['spec/**/*_spec.rb'] end desc "Print Specdoc for all specs" RSpec::Core::RakeTask.new(:doc) do |t| t.rspec_opts = ["--format", "specdoc", "--dry-run"] t.pattern = FileList['spec/**/*_spec.rb'] end desc "Run the specs under spec/unit with activesupport loaded" RSpec::Core::RakeTask.new(:activesupport) do |t| t.rspec_opts = ["--require active_support/core_ext"] t.pattern = FileList['spec/unit/**/*_spec.rb'] end [:unit, :functional, :integration, :stress].each do |sub| desc "Run the specs under spec/#{sub}" RSpec::Core::RakeTask.new(sub) do |t| t.pattern = FileList["spec/#{sub}/**/*_spec.rb"] end end end rescue LoadError STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n" end chef-12.3.0/CONTRIBUTING.md0000644000004100000410000002501212520074675014773 0ustar www-datawww-data# Contributing to Chef We are glad you want to contribute to Chef! We utilize **Github Issues** for issue tracking and contributions. You can contribute in two ways: 1. Reporting an issue or making a feature request [here](#issues). 2. Adding features or fixing bugs yourself and contributing your code to Chef. ## Contribution Process We have a 3 step process that utilizes **Github Issues**: 1. Sign or be added to an existing [Contributor License Agreement (CLA)](https://supermarket.getchef.com/become-a-contributor). 2. Create a Github Pull Request. 3. Do [Code Review](#cr) with the **Chef Engineering Team** or **Chef Core Committers** on the pull request. ### Chef Pull Requests Chef is built to last. We strive to ensure high quality throughout the Chef experience. In order to ensure this, we require a couple of things for all pull requests to Chef: 1. **Tests:** To ensure high quality code and protect against future regressions, we require all the code in Chef to have at least unit test coverage. See the [spec/unit](https://github.com/opscode/chef/tree/master/spec/unit) directory for the existing tests and use ```bundle exec rake spec``` to run them. 2. **Green Travis Run:** We use [Travis CI](https://travis-ci.org/) in order to run our tests continuously on all the pull requests. We require the Travis runs to succeed on every pull request before being merged. In addition to this it would be nice to include the description of the problem you are solving with your change. You can use [Chef Issue Template](#issuetemplate) in the description section of the pull request. ### Chef Code Review Process The Chef Code Review process happens on Github pull requests. See [this article](https://help.github.com/articles/using-pull-requests) if you're not familiar with Github Pull Requests. Once you a pull request, the **Chef Engineering Team** or **Chef Core Committers** will review your code and respond to you with any feedback they might have. The process at this point is as follows: 1. 2 thumbs-ups are required from the **Chef Engineering Team** or **Chef Core Committers** for all merges. 2. When ready, your pull request will be tagged with label `Ready For Merge`. 3. Your patch will be merged into `master` including necessary documentation updates and you will be included in `CHANGELOG.md`. Our goal is to have patches merged in 2 weeks after they are marked to be merged. If you would like to learn about when your code will be available in a release of Chef, read more about [Chef Release Process](#release). ### Developer Office Hours We hold regular "office hours" on Google Hangouts On-The-Air that you can join to review contributions together, ask questions about contributing, or just hang out with Chef Software employees. The regularly scheduled Chef hangouts occur on Mondays and Wednesdays at 3pm Eastern / Noon Pacific. The link to join the Hangout or watch it live is usually tweeted from [@ChefOfficeHours](https://twitter.com/ChefOfficeHours) and posted in the #chef IRC channel on irc.freenode.net when the hangout is about to start. You can watch the recordings of the old Code Review hangouts on the [opscodebtm](http://www.youtube.com/opscodebtm) youtube account. ### Contributor License Agreement (CLA) Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. Chef uses [the Apache 2.0 license](https://github.com/opscode/chef/blob/master/LICENSE) to strike a balance between open contribution and allowing you to use the software however you would like to. The license tells you what rights you have that are provided by the copyright holder. It is important that the contributor fully understands what rights they are licensing and agrees to them. Sometimes the copyright holder isn't the contributor, most often when the contributor is doing work for a company. To make a good faith effort to ensure these criteria are met, Chef requires an Individual CLA or a Corporate CLA for contributions. This agreement helps ensure you are aware of the terms of the license you are contributing your copyrighted works under, which helps to prevent the inclusion of works in the projects that the contributor does not hold the rights to share. It only takes a few minutes to complete a CLA, and you retain the copyright to your contribution. You can complete our [Individual CLA](https://supermarket.getchef.com/icla-signatures/new) online. If you're contributing on behalf of your employer and they retain the copyright for your works, have your employer fill out our [Corporate CLA](https://supermarket.getchef.com/ccla-signatures/new) instead. ### Chef Obvious Fix Policy Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA. As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following: * Spelling / grammar fixes * Typo correction, white space and formatting changes * Comment clean up * Bug fixes that change default return values or error codes stored in constants * Adding logging messages or debugging output * Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc. * Moving source files from one directory or package to another **Whenever you invoke the “obvious fix” rule, please say so in your commit message:** ``` ------------------------------------------------------------------------ commit 370adb3f82d55d912b0cf9c1d1e99b132a8ed3b5 Author: danielsdeleo Date: Wed Sep 18 11:44:40 2013 -0700 Fix typo in config file docs. Obvious fix. ------------------------------------------------------------------------ ``` ## Chef Issue Tracking Chef Issue Tracking is handled using Github Issues. If you are familiar with Chef and know the component that is causing you a problem or if you have a feature request on a specific component you can file an issue in the corresponding Github project. All of our Open Source Software can be found in our [Github organization](https://github.com/opscode/). There is also a listing of the various Chef products and where to file issues that can be found in the Chef docs in the [community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports). Otherwise you can file your issue in the [Chef project](https://github.com/opscode/chef/issues) and we will make sure it gets filed against the appropriate project. In order to decrease the back and forth an issues and help us get to the bottom of them quickly we use below issue template. You can copy paste this code into the issue you are opening and edit it accordingly. ``` ### Version: [Version of the project installed] ### Environment: [Details about the environment such as the Operating System, cookbook details, etc...] ### Scenario: [What you are trying to achieve and you can't?] ### Steps to Reproduce: [If you are filing an issue what are the things we need to do in order to repro your problem?] ### Expected Result: [What are you expecting to happen as the consequence of above reproduction steps?] ### Actual Result: [What actually happens after the reproduction steps?] ``` ### Useful Github Queries Contributions go through a review process to improve code quality and avoid regressions. Managing a large number of contributions requires a workflow to provide queues for work such as triage, code review, and merging. A semi-formal process has evolved over the life of the project. Chef maintains this process pending community development and acceptance of an [RFC](https://github.com/opscode/chef-rfc). These queries will help track contributions through this process: * [Issues that are not assigned to a team](https://github.com/opscode/chef/issues?q=is%3Aopen+-label%3AAIX+-label%3ABSD+-label%3Awindows+-label%3A%22Chef+Core%22++-label%3A%22Dev+Tools%22+-label%3AUbuntu+-label%3A%22Enterprise+Linux%22+-label%3A%22Ready+For+Merge%22+-label%3AMac+-label%3ASolaris+) * [Untriaged Issues](https://github.com/opscode/chef/issues?q=is%3Aopen+is%3Aissue+-label%3ABug+-label%3AEnhancement+-label%3A%22Tech+Cleanup%22+-label%3A%22Ready+For+Merge%22) * [PRs to be Reviewed](https://github.com/opscode/chef/labels/Pending%20Maintainer%20Review) * [Suitable for First Contribution](https://github.com/opscode/chef/labels/Easy) ## Chef Release Cycles Our primary shipping vehicle is operating system specific packages that includes all the requirements of Chef. We call these [Omnibus packages](https://github.com/opscode/omnibus-ruby) We also release our software as gems to [Rubygems](https://rubygems.org/) but we strongly recommend using Chef packages since they are the only combination of native libraries & gems required by Chef that we test throughly. Our version numbering closely follows [Semantic Versioning](http://semver.org/) standard. Our standard version numbers look like X.Y.Z which mean: * X is a major release, which may not be fully compatible with prior major releases * Y is a minor release, which adds both new features and bug fixes * Z is a patch release, which adds just bug fixes We frequently make `alpha` and `beta` releases with version numbers that look like `X.Y.Z.alpha.0` or `X.Y.Z.beta.1`. These releases are still well tested but not as throughly as **Minor** or **Patch** releases. We do a `Minor` release approximately every 3 months and `Patch` releases on a when-needed basis for regressions, significant bugs, and security issues. Announcements of releases are available on [Chef Blog](http://www.getchef.com/blog) when they are available. ## Chef Community Chef is made possible by a strong community of developers and system administrators. If you have any questions or if you would like to get involved in the Chef community you can check out: * [chef](http://lists.opscode.com/sympa/info/chef) and [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing lists * [\#chef](https://botbot.me/freenode/chef) and [\#chef-hacking](https://botbot.me/freenode/chef-hacking) IRC channels on irc.freenode.net Also here are some additional pointers to some awesome Chef content: * [Chef Docs](http://docs.opscode.com/) * [Learn Chef](https://learnchef.opscode.com/) * [Chef Inc](http://www.getchef.com/) chef-12.3.0/LICENSE0000644000004100000410000002514212520074675013553 0ustar www-datawww-data Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. chef-12.3.0/README.md0000644000004100000410000001220712520074675014023 0ustar www-datawww-data# Chef [![Code Climate](https://codeclimate.com/github/opscode/chef.png)](https://codeclimate.com/github/opscode/chef) [![Build Status Master](https://travis-ci.org/chef/chef.svg?branch=master)](https://travis-ci.org/chef/chef) [![Build Status Master](https://ci.appveyor.com/api/projects/status/github/chef/chef?branch=master&svg=true&passingText=master%20-%20Ok&pendingText=master%20-%20Pending&failingText=master%20-%20Failing)](https://ci.appveyor.com/project/Chef/chef/branch/master) Want to try Chef? Get started with [learnchef](https://learn.chef.io) * Documentation: [http://docs.chef.io](http://docs.chef.io) * Source: [http://github.com/chef/chef/tree/master](http://github.com/chef/chef/tree/master) * Tickets/Issues: [https://github.com/chef/chef/issues](https://github.com/chef/chef/issues) * IRC: `#chef` and `#chef-hacking` on Freenode * Mailing list: [http://lists.opscode.com](http://lists.opscode.com) Chef is a configuration management tool designed to bring automation to your entire infrastructure. This README focuses on developers who want to modify Chef source code. If you just want to use Chef, check out these resources: * [learnchef](https://learn.chef.io): Getting started guide * [http://docs.chef.io](http://docs.chef.io): Comprehensive User Docs * [Installer Downloads](https://www.chef.io/download-chef-client/): Install Chef as a complete package ## Installing From Git **NOTE:** Unless you have a specific reason to install from source (to try a new feature, contribute a patch, or run chef on an OS for which no package is available), you should head to the [installer page](https://www.chef.io/download-chef-client/) to get a prebuilt package. ### Prerequisites Install these via your platform's preferred method (apt, yum, ports, emerge, etc.): * git * C compiler, header files, etc. On Ubuntu/Debian, use the `build-essential` package. * ruby 2.0.0 or later * rubygems * bundler ### Chef Installation Then get the source and install it: # Clone this repo git clone https://github.com/chef/chef.git # cd into the source tree cd chef # Install dependencies with bundler bundle install # Build a gem rake gem # Install the gem you just built gem install pkg/chef-VERSION.gem ## Contributing/Development Before working on the code, if you plan to contribute your changes, you need to read the [Chef Contributions document](http://docs.chef.io/community_contributions.html). The general development process is: 1. Fork this repo and clone it to your workstation 2. Create a feature branch for your change 3. Write code and tests 4. Push your feature branch to github and open a pull request against master Once your repository is set up, you can start working on the code. We do use TDD with RSpec, so you'll need to get a development environment running. Follow the above procedure ("Installing from Git") to get your local copy of the source running. ## Reporting Issues Issues can be reported by using [GitHub issues](https://github.com/chef/chef/issues). Full details on how to report issues can be found in the [CONTRIBUTING](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-chef-issue-tracking) doc. Note that this repository is primarily for reporting chef-client issues. For reporting issues against other Chef projects, please look up the appropriate repository to report issues against in the Chef docs in the [community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports). If you can't detemine the appropriate place to report an issue, then please open it against the repository you think best fits and it will be directed to the appropriate project. ## Testing We use RSpec for unit/spec tests. It is not necessary to start the development environment to run the specs--they are completely standalone. # Run All the Tests bundle exec rake spec # Run a Single Test File bundle exec rspec spec/PATH/TO/FILE_spec.rb # Run a Subset of Tests bundle exec rspec spec/PATH/TO/DIR When you submit a pull request, we will automatically run the functional and unit tests in spec/functional/ and spec/unit/ respectively. These will be run on Ubuntu through Travis CI, and on Windows through AppVeyor. The status of these runs will be displayed with your pull request. # License Chef - A configuration management system | | | |:---------------------|:-----------------------------------------| | **Author:** | Adam Jacob () | **Copyright:** | Copyright (c) 2008-2015 Chef Software, Inc. | **License:** | Apache License, Version 2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. chef-12.3.0/distro/0000755000004100000410000000000012520074675014046 5ustar www-datawww-datachef-12.3.0/distro/common/0000755000004100000410000000000012520074675015336 5ustar www-datawww-datachef-12.3.0/distro/common/man/0000755000004100000410000000000012520074675016111 5ustar www-datawww-datachef-12.3.0/distro/common/man/man8/0000755000004100000410000000000012520074675016754 5ustar www-datawww-datachef-12.3.0/distro/common/man/man8/chef-solo.80000644000004100000410000002156712520074675020737 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "CHEF-SOLO" "8" "Chef 12.0" "" "chef-solo" .SH NAME chef-solo \- The man page for the chef-solo command line tool. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp chef\-solo is an open source version of the chef\-client that allows using cookbooks with nodes without requiring access to a Chef server\&. chef\-solo runs locally and requires that a cookbook (and any of its dependencies) be on the same physical disk as the node. chef\-solo is a limited\-functionality version of the chef\-client and \fBdoes not support\fP the following: .INDENT 0.0 .IP \(bu 2 Node data storage .IP \(bu 2 Search indexes .IP \(bu 2 Centralized distribution of cookbooks .IP \(bu 2 A centralized API that interacts with and integrates infrastructure components .IP \(bu 2 Authentication or authorization .IP \(bu 2 Persistent attributes .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 chef\-solo can be run as a daemon. .UNINDENT .UNINDENT .sp The chef\-solo executable is run as a command\-line tool. .sp \fBOptions\fP .sp This command has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C chef\-solo OPTION VALUE OPTION VALUE ... .ft P .fi .UNINDENT .UNINDENT .sp This command has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG\fP, \fB\-\-config CONFIG\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-daemonize\fP Use to run the executable as a daemon. This option may not be used in the same command with the \fB\-\-[no\-]fork\fP option. .sp This option is only available on machines that run in UNIX or Linux environments. For machines that are running Microsoft Windows that require similar functionality, use the \fBchef\-client::service\fP recipe in the \fBchef\-client\fP cookbook: \fI\%http://community.opscode.com/cookbooks/chef\-client\fP\&. This will install a chef\-client service under Microsoft Windows using the Windows Service Wrapper\&. .TP .B \fB\-E ENVIRONMENT_NAME\fP, \fB\-\-environment ENVIRONMENT_NAME\fP The name of the environment. .TP .B \fB\-f\fP, \fB\-\-[no\-]fork\fP Use to contain the chef\-client run in a secondary process with dedicated RAM. When the chef\-client run is complete the RAM will be returned to the master process. This option helps ensure that a chef\-client will use a steady amount of RAM over time because the master process will not run recipes. This option will also help prevent memory leaks (such as those that can be introduced by the code contained within a poorly designed cookbook). Use \fB\-\-no\-fork\fP to disable running the chef\-client in fork node. Default value: \fB\-\-fork\fP\&. This option may not be used in the same command with the \fB\-\-daemonize\fP and \fB\-\-interval\fP options. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBdoc\fP (default) or \fBmin\fP\&. .sp Use \fBdoc\fP to print the progress of the chef\-client run using full strings that display a summary of updates as they occur. .sp Use \fBmin\fP to print the progress of the chef\-client run using single characters. A summary of updates is printed at the end of the chef\-client run. A dot (\fB\&.\fP) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (\fB\&.\fP) is printed when the resource is up to date, an \fBS\fP is printed when the resource is skipped by \fBnot_if\fP or \fBonly_if\fP, and a \fBU\fP is printed when the resource is updated. .sp Other formatting options are available when those formatters are configured in the client.rb file using the \fBadd_formatter\fP option. .TP .B \fB\-\-force\-formatter\fP Use to show formatter output instead of logger output. .TP .B \fB\-\-force\-logger\fP Use to show logger output instead of formatter output. .TP .B \fB\-g GROUP\fP, \fB\-\-group GROUP\fP The name of the group that owns a process. This is required when starting any executable as a daemon. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i SECONDS\fP, \fB\-\-interval SECONDS\fP The frequency (in seconds) at which the chef\-client runs. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. This option may not be used in the same command with the \fB\-\-[no\-]fork\fP option. .TP .B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP The path to a file that contains JSON data. .sp Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C "run_list": [ "recipe[base]", "recipe[foo]", "recipe[bar]", "role[webserver]" ], .ft P .fi .UNINDENT .UNINDENT .sp may be used by running \fBchef\-client \-j path/to/file.json\fP\&. .sp In certain situations this option may be used to update \fBnormal\fP attributes. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } .ft P .fi .UNINDENT .UNINDENT .sp will result in a node object similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "maybe\-dev\-99", "normal": { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } } .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP The level of logging that will be stored in a log file. .TP .B \fB\-L LOGLOCATION\fP, \fB\-\-logfile c\fP The location in which log file output files will be saved. If this location is set to something other than \fBSTDOUT\fP, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. Default setting: \fB\-\-color\fP\&. .TP .B \fB\-N NODE_NAME\fP, \fB\-\-node\-name NODE_NAME\fP The name of the node. .TP .B \fB\-o RUN_LIST_ITEM\fP, \fB\-\-override\-runlist RUN_LIST_ITEM\fP Replace the current run list with the specified items. .TP .B \fB\-r RECIPE_URL\fP, \fB\-\-recipe\-url RECIPE_URL\fP The URL location from which a remote cookbook tar.gz will be downloaded. .TP .B \fB\-\-run\-lock\-timeout SECONDS\fP The amount of time (in seconds) to wait for a chef\-client run to finish. Default value: not set (indefinite). Set to \fB0\fP to cause a second chef\-client to exit immediately. .TP .B \fB\-s SECONDS\fP, \fB\-\-splay SECONDS\fP A number (in seconds) to add to the \fBinterval\fP that is used to determine the frequency of chef\-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user that owns a process. This is required when starting any executable as a daemon. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-W\fP, \fB\-\-why\-run\fP Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-solo \-c ~/solo.rb \-j ~/node.json \-r http://www.example.com/chef\-solo.tar.gz .ft P .fi .UNINDENT .UNINDENT .sp The tar.gz archived into the \fBfile_cache_path\fP, and then extracted to \fBcookbooks_path\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-solo \-c ~/solo.rb \-j ~/node.json .ft P .fi .UNINDENT .UNINDENT .sp chef\-solo will look in the solo.rb file to determine the directory in which cookbooks are located. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-solo \-c ~/solo.rb \-j http://www.example.com/node.json \-r http://www.example.com/chef\-solo.tar.gz .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man8/chef-apply.80000644000004100000410000000405612520074675021102 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "CHEF-APPLY" "8" "Chef 12.0" "" "chef-client" .SH NAME chef-apply \- The man page for the chef-apply command line tool. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp chef\-apply allows a single recipe to be run from the command line. .SH OPTIONS .sp This command has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C chef\-apply name_of_recipe.rb .ft P .fi .UNINDENT .UNINDENT .sp This tool has the following options: .INDENT 0.0 .TP .B \fB\-e RECIPE_TEXT\fP, \fB\-\-execute RECIPE_TEXT\fP Use to execute a resource using a string. .TP .B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP The level of logging that will be stored in a log file. .TP .B \fB\-s\fP, \fB\-\-stdin\fP Use to execute a resource using standard input. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-W\fP, \fB\-\-why\-run\fP Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .UNINDENT .SH EXAMPLES .sp To use chef\-apply to run a recipe named \fBmachinations.rb\fP, enter the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-apply machinations.rb .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man8/chef-client.80000644000004100000410000003160012520074675021226 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "CHEF-CLIENT" "8" "Chef 12.0" "" "chef-client" .SH NAME chef-client \- The man page for the chef-client command line tool. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A chef\-client is an agent that runs locally on every node that is under management by Chef\&. When a chef\-client is run, it will perform all of the steps that are required to bring the node into the expected state, including: .INDENT 0.0 .IP \(bu 2 Registering and authenticating the node with the Chef server .IP \(bu 2 Building the node object .IP \(bu 2 Synchronizing cookbooks .IP \(bu 2 Compiling the resource collection by loading each of the required cookbooks, including recipes, attributes, and all other dependencies .IP \(bu 2 Taking the appropriate and required actions to configure the node .IP \(bu 2 Looking for exceptions and notifications, handling each as required .UNINDENT .sp The chef\-client executable is run as a command\-line tool. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 A client.rb file is used to specify the configuration details for the chef\-client\&. .INDENT 0.0 .IP \(bu 2 This file is loaded every time this executable is run .IP \(bu 2 On UNIX\- and Linux\-based machines, the default location for this file is \fB/etc/chef/client.rb\fP; on Microsoft Windows machines, the default location for this file is \fBC:\echef\eclient.rb\fP; use the \fB\-\-config\fP option from the command line to change this location .IP \(bu 2 This file is not created by default .IP \(bu 2 When a client.rb file is present in this directory, the settings contained within that file will override the default configuration settings .UNINDENT .UNINDENT .UNINDENT .SH OPTIONS .sp This command has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C chef\-client OPTION VALUE OPTION VALUE ... .ft P .fi .UNINDENT .UNINDENT .sp This command has the following options: .INDENT 0.0 .TP .B \fB\-A\fP, \fB\-\-fatal\-windows\-admin\-check\fP Use to cause a chef\-client run to fail when the chef\-client does not have administrator privileges in Microsoft Windows\&. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. If a port is not specified\-\-\-individually, as range of ports, or from the \fBchef_zero.port\fP setting in the client.rb file\-\-\-the chef\-client will scan for ports between 8889\-9999 and will pick the first port that is available. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBdoc\fP (default) or \fBmin\fP\&. .sp Use \fBdoc\fP to print the progress of the chef\-client run using full strings that display a summary of updates as they occur. .sp Use \fBmin\fP to print the progress of the chef\-client run using single characters. A summary of updates is printed at the end of the chef\-client run. A dot (\fB\&.\fP) is printed for events that do not have meaningful status information, such as loading a file or synchronizing a cookbook. For resources, a dot (\fB\&.\fP) is printed when the resource is up to date, an \fBS\fP is printed when the resource is skipped by \fBnot_if\fP or \fBonly_if\fP, and a \fBU\fP is printed when the resource is updated. .sp Other formatting options are available when those formatters are configured in the client.rb file using the \fBadd_formatter\fP option. .TP .B \fB\-\-force\-formatter\fP Use to show formatter output instead of logger output. .TP .B \fB\-\-force\-logger\fP Use to show logger output instead of formatter output. .TP .B \fB\-g GROUP\fP, \fB\-\-group GROUP\fP The name of the group that owns a process. This is required when starting any executable as a daemon. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i SECONDS\fP, \fB\-\-interval SECONDS\fP The frequency (in seconds) at which the chef\-client runs. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. Default value: \fB1800\fP\&. .TP .B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP The path to a file that contains JSON data. .sp Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C "run_list": [ "recipe[base]", "recipe[foo]", "recipe[bar]", "role[webserver]" ], .ft P .fi .UNINDENT .UNINDENT .sp may be used by running \fBchef\-client \-j path/to/file.json\fP\&. .sp In certain situations this option may be used to update \fBnormal\fP attributes. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } .ft P .fi .UNINDENT .UNINDENT .sp will result in a node object similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "maybe\-dev\-99", "normal": { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } } .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B \fB\-k KEY_FILE\fP, \fB\-\-client_key KEY_FILE\fP The location of the file which contains the client key. Default value: \fB/etc/chef/client.pem\fP\&. .TP .B \fB\-K KEY_FILE\fP, \fB\-\-validation_key KEY_FILE\fP The location of the file which contains the key used when a chef\-client is registered with a Chef server\&. A validation key is signed using the \fBvalidation_client_name\fP for authentication. Default value: \fB/etc/chef/validation.pem\fP\&. .TP .B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP The level of logging that will be stored in a log file. .TP .B \fB\-L LOGLOCATION\fP, \fB\-\-logfile c\fP The location in which log file output files will be saved. If this location is set to something other than \fBSTDOUT\fP, standard output logging will still be performed (otherwise there would be no output other than to a file). This is recommended when starting any executable as a daemon. Default value: \fBSTDOUT\fP\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. Default setting: \fB\-\-color\fP\&. .TP .B \fB\-N NODE_NAME\fP, \fB\-\-node\-name NODE_NAME\fP The name of the node. .TP .B \fB\-o RUN_LIST_ITEM\fP, \fB\-\-override\-runlist RUN_LIST_ITEM\fP Replace the current run list with the specified items. This option will not clear the list of cookbooks (and related files) that is cached on the node. .TP .B \fB\-\-once\fP Use to run the chef\-client only once and to cancel \fBinterval\fP and \fBsplay\fP options. .TP .B \fB\-P PID_FILE\fP, \fB\-\-pid PID_FILE\fP The location in which a process identification number (pid) is saved. An executable, when started as a daemon, will write the pid to the specified file. Default value: \fB/tmp/name\-of\-executable.pid\fP\&. .TP .B \fB\-r RUN_LIST_ITEM\fP, \fB\-\-runlist RUN_LIST_ITEM\fP Use to permanently replace the current run\-list with the specified run\-list items. .TP .B \fB\-R\fP, \fB\-\-enable\-reporting\fP Use to enable Chef reporting, which performs data collection during a chef\-client run. .TP .B \fBRECIPE_FILE\fP The path to a recipe. For example, if a recipe file is in the current directory, use \fBrecipe_file.rb\fP\&. This is typically used with the \fB\-\-local\-mode\fP option. .TP .B \fB\-\-run\-lock\-timeout SECONDS\fP The amount of time (in seconds) to wait for a chef\-client run to finish. Default value: not set (indefinite). Set to \fB0\fP to cause a second chef\-client to exit immediately. .TP .B \fB\-s SECONDS\fP, \fB\-\-splay SECONDS\fP A number (in seconds) to add to the \fBinterval\fP that is used to determine the frequency of chef\-client runs. This number can help prevent server load when there are many clients running at the same time. When the chef\-client is run at intervals, \fB\-\-splay\fP and \fB\-\-interval\fP values are applied before the chef\-client run. .TP .B \fB\-S CHEF_SERVER_URL\fP, \fB\-\-server CHEF_SERVER_URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user that owns a process. This is required when starting any executable as a daemon. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-W\fP, \fB\-\-why\-run\fP Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH RUN WITH ELEVATED PRIVILEGES .sp The chef\-client may need to be run with elevated privileges in order to get a recipe to converge correctly. On UNIX and UNIX\-like operating systems this can be done by running the command as root. On Microsoft Windows this can be done by running the command prompt as an administrator. .SS Linux .sp On Linux, the following error sometimes occurs when the permissions used to run the chef\-client are incorrect: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-client [Tue, 29 Nov 2011 19:46:17 \-0800] INFO: *** Chef 10.X.X *** [Tue, 29 Nov 2011 19:46:18 \-0800] WARN: Failed to read the private key /etc/chef/client.pem: # .ft P .fi .UNINDENT .UNINDENT .sp This can be resolved by running the command as root. There are a few ways this can be done: .INDENT 0.0 .IP \(bu 2 Log in as root and then run the chef\-client .IP \(bu 2 Use \fBsu\fP to become the root user, and then run the chef\-client\&. For example: .INDENT 2.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ su .ft P .fi .UNINDENT .UNINDENT .sp and then: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ chef\-client .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .IP \(bu 2 Use the sudo utility .INDENT 2.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sudo chef\-client .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .IP \(bu 2 Give a user access to read \fB/etc/chef\fP and also the files accessed by the chef\-client\&. This requires super user privileges and, as such, is not a recommended approach .UNINDENT .SS Windows .sp On Microsoft Windows, running without elevated privileges (when they are necessary) is an issue that fails silently. It will appear that the chef\-client completed its run successfully, but the changes will not have been made. When this occurs, do one of the following to run the chef\-client as the administrator: .INDENT 0.0 .IP \(bu 2 Log in to the administrator account. (This is not the same as an account in the administrator\(aqs security group.) .IP \(bu 2 Run the chef\-client process from the administrator account while being logged into another account. Run the following command: .INDENT 2.0 .INDENT 3.5 .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ runas /user:Administrator "cmd /C chef\-client" .ft P .fi .UNINDENT .UNINDENT .sp This will prompt for the administrator account password. .UNINDENT .UNINDENT .IP \(bu 2 Open a command prompt by right\-clicking on the command prompt application, and then selecting \fBRun as administrator\fP\&. After the command window opens, the chef\-client can be run as the administrator .UNINDENT .SH EXAMPLES .sp \fBStart a Chef run when the chef\-client is running as a daemon\fP .sp A chef\-client that is running as a daemon can be woken up and started by sending the process a \fBSIGUSR1\fP\&. For example, to trigger a chef\-client run on a machine running Linux: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sudo killall \-USR1 chef\-client .ft P .fi .UNINDENT .UNINDENT .sp \fBStart a Chef run manually\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ps auxw|grep chef\-client .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C root 66066 0.9 0.0 2488880 264 s001 S+ 10:26AM 0:03.05 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /usr/bin/chef\-client \-i 3600 \-s 20 .ft P .fi .UNINDENT .UNINDENT .sp and then enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sudo kill \-USR1 66066 .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/0000755000004100000410000000000012520074675016745 5ustar www-datawww-datachef-12.3.0/distro/common/man/man1/knife-cookbook.10000644000004100000410000004555412520074675021744 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-COOKBOOK" "1" "Chef 12.0" "" "knife cookbook" .SH NAME knife-cookbook \- The man page for the knife cookbook subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A cookbook is the fundamental unit of configuration and policy distribution. Each cookbook defines a scenario, such as everything needed to install and configure MySQL, and then it contains all of the components that are required to support that scenario, including: .INDENT 0.0 .IP \(bu 2 Attribute values that are set on nodes .IP \(bu 2 Definitions that allow the creation of reusable collections of resources .IP \(bu 2 File distributions .IP \(bu 2 Libraries that extend the chef\-client and/or provide helpers to Ruby code .IP \(bu 2 Recipes that specify which resources to manage and the order in which those resources will be applied .IP \(bu 2 Custom resources and providers .IP \(bu 2 Templates .IP \(bu 2 Versions .IP \(bu 2 Metadata about recipes (including dependencies), version constraints, supported platforms, and so on .UNINDENT .sp The \fBknife cookbook\fP subcommand is used to interact with cookbooks that are located on the Chef server or the local chef\-repo\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife cookbook\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH BULK DELETE .sp The \fBbulk delete\fP argument is used to delete cookbook files that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook bulk delete REGEX (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-p\fP, \fB\-\-purge\fP Use to entirely remove a cookbook (or cookbook version) from the Chef server\&. This action should be used carefully because only one copy of any single file is stored on the Chef server\&. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged. .UNINDENT .sp \fBExamples\fP .sp Use a regular expression to define the pattern used to bulk delete cookbooks: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook bulk delete "^[0\-9]{3}$" \-p .ft P .fi .UNINDENT .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to create a new cookbook directory on the local machine, including the following directories and files: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 cookbook/attributes .IP \(bu 2 cookbook/CHANGELOG.md .IP \(bu 2 cookbook/definitions .IP \(bu 2 cookbook/files/default .IP \(bu 2 cookbook/libraries .IP \(bu 2 cookbook/metadata.rb .IP \(bu 2 cookbook/providers .IP \(bu 2 cookbook/README.md (or .rdoc) .IP \(bu 2 cookbook/recipes/default.rb .IP \(bu 2 cookbook/resources .IP \(bu 2 cookbook/templates/default .UNINDENT .UNINDENT .UNINDENT .sp After the cookbook is created, it can be uploaded to the Chef server using the \fBknife upload\fP argument. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook create COOKBOOK_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-C COPYRIGHT_HOLDER\fP, \fB\-\-copyright COPYRIGHT_HOLDER\fP The name of the copyright holder. This option will place a copyright notice that contains the name of the copyright holder in each of the pre\-created files. If this option is not specified, a copyright name of "your_company_name" will be used instead; it can be easily modified later. .TP .B \fB\-I LICENSE\fP, \fB\-\-license LICENSE\fP The type of license under which a cookbook is distributed: \fBapachev2\fP, \fBgplv2\fP, \fBgplv3\fP, \fBmit\fP, or \fBnone\fP (default). This option will place the appropriate license notice in the pre\-created files: \fBApache v2.0\fP (for \fBapachev2\fP), \fBGPL v2\fP (for \fBgplv2\fP), \fBGPL v3\fP (for \fBgplv3\fP), \fBMIT\fP (for \fBmit\fP), or \fBlicense \(aqProprietary \- All Rights Reserved\fP (for \fBnone\fP). Be aware of the licenses for files inside of a cookbook and be sure to follow any restrictions they describe. .TP .B \fB\-m EMAIL\fP, \fB\-\-email EMAIL\fP The email address for the individual who maintains the cookbook. This option will place an email address in each of the pre\-created files. If this option is not specified, an email name of "your_email" will be used instead; it can be easily modified later. .TP .B \fB\-o PATH\fP, \fB\-\-cookbook\-path PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .TP .B \fB\-r FORMAT\fP, \fB\-\-readme\-format FORMAT\fP The document format of the readme file: \fBmd\fP (markdown) and \fBrdoc\fP (Ruby docs). .UNINDENT .sp \fBExamples\fP .sp To create a cookbook named "my_cookbook" with copyright, email, license, and readme format options specified, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook create my_cookbook \-C "My Name" \-m "my@email.com" \-I apachev2 \-r md .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ** Creating cookbook my_cookbook ** Creating README for cookbook: my_cookbook ** Creating metadata for cookbook: my_cookbook .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete a specified cookbook or cookbook version on the Chef server (and not locally). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook delete COOKBOOK_NAME [COOKBOOK_VERSION] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to delete all cookbooks (and cookbook versions). .TP .B \fBCOOKBOOK_VERSION\fP The version of a cookbook to be deleted. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, knife will prompt for a version. .TP .B \fB\-p\fP, \fB\-\-purge\fP Use to entirely remove a cookbook (or cookbook version) from the Chef server\&. This action should be used carefully because only one copy of any single file is stored on the Chef server\&. Consequently, purging a cookbook will disable any other cookbook that references one or more files from a cookbook that has been purged. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook delete cookbook_name version .ft P .fi .UNINDENT .UNINDENT .sp For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook delete smartmon 0.8 .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH DOWNLOAD .sp The \fBdownload\fP argument is used to download a cookbook from the Chef server to the current working directory. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook download COOKBOOK_NAME [COOKBOOK_VERSION] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-d DOWNLOAD_DIRECTORY\fP, \fB\-\-dir DOWNLOAD_DIRECTORY\fP The directory in which cookbooks are located. .TP .B \fB\-f\fP, \fB\-\-force\fP Use to overwrite an existing directory. .TP .B \fB\-N\fP, \fB\-\-latest\fP Use to download the most recent version of a cookbook. .UNINDENT .sp \fBExamples\fP .sp To download a cookbook named "smartmon", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook download smartmon .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view a list of cookbooks that are currently available on the Chef server\&. The list will contain only the most recent version for each cookbook by default. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook list (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to return all available versions for every cookbook. .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To view a list of cookbooks: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook list .ft P .fi .UNINDENT .UNINDENT .SH METADATA .sp The \fBmetadata\fP argument is used to generate the metadata for one or more cookbooks. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook metadata (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to generate metadata for all cookbooks. .TP .B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook metadata \-a .ft P .fi .UNINDENT .UNINDENT .SH METADATA FROM FILE .sp The \fBmetadata from file\fP argument is used to load the metadata for a cookbook from a file. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook metadata from file FILE .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook metadata from file /path/to/file .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to view information about a cookbook, parts of a cookbook (attributes, definitions, files, libraries, providers, recipes, resources, and templates), or a file that is associated with a cookbook (including attributes such as checksum or specificity). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook show COOKBOOK_NAME [COOKBOOK_VERSION] [PART...] [FILE_NAME] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBCOOKBOOK_VERSION\fP The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned. .TP .B \fB\-f FQDN\fP, \fB\-\-fqdn FQDN\fP The FQDN of the host. .TP .B \fBFILE_NAME\fP The name of a file that is associated with a cookbook. .TP .B \fB\-p PLATFORM\fP, \fB\-\-platform PLATFORM\fP The platform for which a cookbook is designed. .TP .B \fBPART\fP The part of the cookbook to show: \fBattributes\fP, \fBdefinitions\fP, \fBfiles\fP, \fBlibraries\fP, \fBproviders\fP, \fBrecipes\fP, \fBresources\fP, or \fBtemplates\fP\&. More than one part can be specified. .TP .B \fB\-V PLATFORM_VERSION\fP, \fB\-\-platform\-version PLATFORM_VERSION\fP The version of the platform. .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To get the list of available versions of a cookbook named "getting\-started", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook show getting\-started .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C getting\-started 0.3.0 0.2.0 .ft P .fi .UNINDENT .UNINDENT .sp To show a list of data about a cookbook using the name of the cookbook and the version, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook show getting\-started 0.3.0 .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C attributes: checksum: fa0fc4abf3f6787aeb5c3c5c35de667c name: default.rb path: attributes/default.rb specificity: default url: https://somelongurlhere.com chef_type: cookbook_version cookbook_name: getting\-started definitions: [] files: [] frozen?: false json_class: Chef::CookbookVersion libraries: [] .ft P .fi .UNINDENT .UNINDENT .sp To only view data about "templates", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook show getting\-started 0.3.0 templates .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C checksum: a29d6f254577b830091f140c3a78b1fe name: chef\-getting\-started.txt.erb path: templates/default/chef\-getting\-started.txt.erb specificity: default url: https://someurlhere.com .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH TEST .sp The \fBtest\fP argument is used to test a cookbook for syntax errors. This argument uses Ruby syntax checking to verify every file in a cookbook that ends in .rb and Embedded Ruby (ERB)\&. This argument will respect \&.chefignore files when determining which cookbooks to test for syntax errors. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook test COOKBOOK_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to test all cookbooks. .TP .B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook test cookbook_name .ft P .fi .UNINDENT .UNINDENT .SH UPLOAD .sp The \fBupload\fP argument is used to upload one or more cookbooks (and any files that are associated with those cookbooks) from a local repository to the Chef server\&. Only files that do not already exist on the Chef server will be uploaded. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Use a \&.chefignore file to prevent the upload of specific files and file types, such as temporary files or files placed in folders by version control systems. The \&.chefignore file must be located in the root of the cookbook repository and must use rules similar to filename globbing (as defined by the Ruby \fBFile.fnmatch\fP syntax). .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Empty directories are not uploaded to the Chef server\&. To upload an empty directory, create a "dot" file\-\-\-e.g. \fB\&.keep\fP\-\-\-in that directory to ensure that the directory itself is not empty. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook upload [COOKBOOK_NAME...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to upload all cookbooks. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-include\-dependencies\fP Use to ensure that when a cookbook has a dependency on one (or more) cookbooks, those cookbooks will also be uploaded. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP Use to set the environment version dependency to the cookbook version being uploaded. .TP .B \fB\-\-force\fP Use to update a cookbook even if the \fB\-\-freeze\fP flag has been set. .TP .B \fB\-\-freeze\fP Use to require changes to a cookbook be included as a new version. Only the \fB\-\-force\fP option can override this setting. .TP .B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook upload cookbook_name .ft P .fi .UNINDENT .UNINDENT .sp To upload a cookbook, and then prevent other users from being able to make changes to it, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook upload redis \-\-freeze .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Uploading redis... Upload completed .ft P .fi .UNINDENT .UNINDENT .sp If a cookbook is frozen and the \fB\-\-force\fP option is not specified, knife will return an error message similar to the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Uploading redis... ERROR: Version 0.1.6 of cookbook redis is frozen. Use \-\-force to override. .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-node.10000644000004100000410000003175312520074675021057 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-NODE" "1" "Chef 12.0" "" "knife node" .SH NAME knife-node \- The man page for the knife node subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A node is any physical, virtual, or cloud machine that is configured to be maintained by a chef\-client\&. .sp The \fBknife node\fP subcommand is used to manage the nodes that exist on a Chef server\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife node\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH BULK DELETE .sp The \fBbulk delete\fP argument is used to delete one or more nodes that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node bulk delete REGEX .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp Use a regular expression to define the pattern used to bulk delete nodes: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node bulk delete "^[0\-9]{3}$" .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH CREATE .sp The \fBcreate\fP argument is used to add a node to the Chef server\&. Node data is stored as JSON on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node create NODE_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To add a node, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node create node1 .ft P .fi .UNINDENT .UNINDENT .sp In the $EDITOR enter the node data in JSON: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "normal": { }, "name": "foobar", "override": { }, "default": { }, "json_class": "Chef::Node", "automatic": { }, "run_list": [ "recipe[zsh]", "role[webserver]" ], "chef_type": "node" } .ft P .fi .UNINDENT .UNINDENT .sp When finished, save it. .SH DELETE .sp The \fBdelete\fP argument is used to delete a node from the Chef server\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Deleting a node will not delete any corresponding API clients. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node delete NODE_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node delete node_name .ft P .fi .UNINDENT .UNINDENT .SH EDIT .sp The \fBedit\fP argument is used to edit the details of a node on a Chef server\&. Node data is stored as JSON on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node edit NODE_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Displays a node in the $EDITOR\&. By default, attributes that are default, override, or automatic are not shown. .UNINDENT .sp \fBExamples\fP .sp To edit the data for a node named \fBnode1\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node edit node1 \-a .ft P .fi .UNINDENT .UNINDENT .sp Update the role data in JSON: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "normal": { }, "name": "node1", "override": { }, "default": { }, "json_class": "Chef::Node", "automatic": { }, "run_list": [ "recipe[devops]", "role[webserver]" ], "chef_type": "node" } .ft P .fi .UNINDENT .UNINDENT .sp When finished, save it. .SH FROM FILE .sp The \fBfrom file\fP argument is used to create a node using existing node data as a template. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node from file FILE .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To add a node using data contained in a JSON file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node from file "path to JSON file" .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view all of the nodes that exist on a Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node list (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To verify the list of nodes that are registered with the Chef server, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node list .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C i\-12345678 rs\-123456 .ft P .fi .UNINDENT .UNINDENT .SH RUN_LIST ADD .sp The \fBrun_list add\fP argument is used to add run\-list items (roles or recipes) to a node. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add NODE_NAME RUN_LIST_ITEM (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a ITEM\fP, \fB\-\-after ITEM\fP Use this to add the run list item after the specified run list item. .TP .B \fB\-b ITEM\fP, \fB\-\-before ITEM\fP Use this to add the run list item before the specified run list item. .UNINDENT .sp \fBExamples\fP .sp To add a role to a run\-list, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add node \(aqrole[ROLE_NAME]\(aq .ft P .fi .UNINDENT .UNINDENT .sp To add roles and recipes to a run\-list, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add node \(aqrecipe[COOKBOOK::RECIPE_NAME],recipe[COOKBOOK::RECIPE_NAME],role[ROLE_NAME]\(aq .ft P .fi .UNINDENT .UNINDENT .sp To add a recipe to a run\-list using the fully qualified format, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add node \(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq .ft P .fi .UNINDENT .UNINDENT .sp To add a recipe to a run\-list using the cookbook format, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add node \(aqCOOKBOOK::RECIPE_NAME\(aq .ft P .fi .UNINDENT .UNINDENT .sp To add the default recipe of a cookbook to a run\-list, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list add node \(aqCOOKBOOK\(aq .ft P .fi .UNINDENT .UNINDENT .SH RUN_LIST REMOVE .sp The \fBrun_list remove\fP argument is used to remove run\-list items (roles or recipes) from a node. A recipe must be in one of the following formats: fully qualified, cookbook, or default. Both roles and recipes must be in quotes, for example: \fB\(aqrole[ROLE_NAME]\(aq\fP or \fB\(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq\fP\&. Use a comma to separate roles and recipes when removing more than one, like this: \fB\(aqrecipe[COOKBOOK::RECIPE_NAME],COOKBOOK::RECIPE_NAME,role[ROLE_NAME]\(aq\fP\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list remove NODE_NAME RUN_LIST_ITEM .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To remove a role from a run\-list, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list remove node \(aqrole[ROLE_NAME]\(aq .ft P .fi .UNINDENT .UNINDENT .sp To remove a recipe from a run\-list using the fully qualified format, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node run_list remove node \(aqrecipe[COOKBOOK::RECIPE_NAME]\(aq .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to display information about a node. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node show NODE_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .TP .B \fB\-l\fP, \fB\-\-long\fP Use to display all attributes in the output and to show the output as JSON\&. .TP .B \fB\-m\fP, \fB\-\-medium\fP Use to display normal attributes in the output and to show the output as JSON\&. .TP .B \fB\-r\fP, \fB\-\-run\-list\fP Use to show only the run\-list. .UNINDENT .sp \fBExamples\fP .sp To view all data for a node named \fBbuild\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node show build .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Node Name: build Environment: _default FQDN: IP: Run List: Roles: Recipes: Platform: .ft P .fi .UNINDENT .UNINDENT .sp To show basic information about a node, truncated and nicely formatted: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife node show .ft P .fi .UNINDENT .UNINDENT .sp To show all information about a node, nicely formatted: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife node show \-l .ft P .fi .UNINDENT .UNINDENT .sp To list a single node attribute: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife node show \-a .ft P .fi .UNINDENT .UNINDENT .sp where \fB\fP is something like kernel or platform. (This doesn\(aqt work for nested attributes like \fBnode[kernel][machine]\fP because \fBknife node show\fP doesn\(aqt understand nested attributes.) .sp To view the FQDN for a node named \fBi\-12345678\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node show i\-12345678 \-a fqdn .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C fqdn: ip\-10\-251\-75\-20.ec2.internal .ft P .fi .UNINDENT .UNINDENT .sp To view the run list for a node named \fBdev\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife node show dev \-r .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .sp To view node information in raw JSON, use the \fB\-l\fP or \fB\-\-long\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife node show \-l \-F json .ft P .fi .UNINDENT .UNINDENT .sp and/or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife node show \-l \-\-format=json .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-deps.10000644000004100000410000001355112520074675021061 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-DEPS" "1" "Chef 12.0" "" "knife deps" .SH NAME knife-deps \- The man page for the knife deps subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife deps\fP subcommand is used to identify dependencies for a node, role, or cookbook. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-[no\-]recurse\fP Use \fB\-\-recurse\fP to list dependencies recursively. This option can only be used when \fB\-\-tree\fP is set to \fBtrue\fP\&. Default: \fB\-\-no\-recurse\fP\&. .TP .B \fB\-\-remote\fP Use to determine dependencies from objects located on the Chef server instead of in the local chef\-repo\&. Default: \fBfalse\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-\-tree\fP Use to show dependencies in a visual tree structure (including duplicates, if they exist). Default: \fBfalse\fP\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps nodes/node_name.json .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps roles/role_name.json .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps cookbooks/cookbook_name.json .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps environments/environment_name.json .ft P .fi .UNINDENT .UNINDENT .sp To find the dependencies for a combination of nodes, cookbooks, roles, and/or environments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps cookbooks/git.json cookbooks/github.json roles/base.json environments/desert.json nodes/mynode.json .ft P .fi .UNINDENT .UNINDENT .sp A wildcard can be used to return all of the child nodes. For example, all of the environments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps environments/*.json .ft P .fi .UNINDENT .UNINDENT .sp Use the \fB\-\-tree\fP option to view the results with structure: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps roles/webserver.json .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C roles/webserver.json roles/base.json cookbooks/github cookbooks/git cookbooks/users cookbooks/apache2 .ft P .fi .UNINDENT .UNINDENT .sp The output of \fBknife deps\fP can be passed to \fBknife upload\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload \(gaknife deps nodes/*.json .ft P .fi .UNINDENT .UNINDENT .sp The output of \fBknife deps\fP can be passed to \fBknife xargs\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife deps nodes/*.json | xargs knife upload .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-ssl-check.10000644000004100000410000001453312520074675022003 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SSL-CHECK" "1" "Chef 12.0" "" "knife ssl check" .SH NAME knife-ssl-check \- The man page for the knife ssl check subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife ssl check\fP subcommand is used to verify the SSL configuration for the Enterprise Chef and/or Open Source Chef servers, or at another location specified by a URL or URI. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 When verification of a remote server\(aqs SSL certificate is disabled, the chef\-client will issue a warning similar to "SSL validation of HTTPS requests is disabled. HTTPS connections are still encrypted, but the chef\-client is not able to detect forged replies or man\-in\-the\-middle attacks." To configure SSL for the chef\-client, set \fBssl_verify_mode\fP to \fB:verify_peer\fP (recommended) \fBor\fP \fBverify_api_cert\fP to \fBtrue\fP in the client.rb file. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl check URI .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname. .TP .B \fB\-A\fP, \fB\-\-forward\-agent\fP Use to enable SSH agent forwarding. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP The number of allowed concurrent connections. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP The SSH identity file used for authentication. Key\-based authentication is recommended. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-m\fP, \fB\-\-manual\-list\fP Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP .TP .B \fB\-\-[no\-]host\-key\-verify\fP Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&. .TP .B \fBOTHER\fP The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.) .TP .B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP The SSH port. .TP .B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fBSEARCH_QUERY\fP The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command. .TP .B \fBSSH_COMMAND\fP The command that will be run against the results of a search query. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP The SSH user name. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp The following examples show how to use this knife subcommand: .sp \fBVerify the SSL configuration for the Chef server\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl check .ft P .fi .UNINDENT .UNINDENT .sp \fBVerify the SSL configuration for the chef\-client\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl check \-c /etc/chef/client.rb .ft P .fi .UNINDENT .UNINDENT .sp \fBVerify an external server\(aqs SSL certificate\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl check URL_or_URI .ft P .fi .UNINDENT .UNINDENT .sp for example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl check https://www.getchef.com .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-exec.10000644000004100000410000001750412520074675021054 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-EXEC" "1" "Chef 12.0" "" "knife exec" .SH NAME knife-exec \- The man page for the knife exec subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife exec\fP subcommand uses the knife configuration file to execute Ruby scripts in the context of a fully configured chef\-client\&. This subcommand is most often used to run scripts that will only access Chef server one time (or otherwise very infrequently). Use this subcommand any time that an operation does not warrant full usage of the knife subcommand library. .sp \fBAuthenticated API Requests\fP .sp The \fBknife exec\fP subcommand can be used to make authenticated API requests to the Chef server using the following methods: .TS center; |l|l|. _ T{ Method T} T{ Description T} _ T{ \fBapi.delete\fP T} T{ Use to delete an object from the Chef server\&. T} _ T{ \fBapi.get\fP T} T{ Use to get the details of an object on the Chef server\&. T} _ T{ \fBapi.post\fP T} T{ Use to add an object to the Chef server\&. T} _ T{ \fBapi.put\fP T} T{ Use to update an object on the Chef server\&. T} _ .TE .sp These methods are used with the \fB\-E\fP option, which executes that string locally on the workstation using chef\-shell\&. These methods have the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqapi.method(/endpoint)\(aq .ft P .fi .UNINDENT .UNINDENT .sp where: .INDENT 0.0 .IP \(bu 2 \fBapi.method\fP is the corresponding authentication method \-\-\- \fBapi.delete\fP, \fBapi.get\fP, \fBapi.post\fP, or \fBapi.put\fP .IP \(bu 2 \fB/endpoint\fP is an endpoint in the Chef server API .UNINDENT .sp For example, to get the data for a node named "Example_Node": .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqputs api.get("/nodes/Example_Node")\(aq .ft P .fi .UNINDENT .UNINDENT .sp and to ensure that the output is visible in the console, add the \fBputs\fP in front of the API authorization request: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqputs api.get("/nodes/Example_Node")\(aq .ft P .fi .UNINDENT .UNINDENT .sp where \fBputs\fP is the shorter version of the \fB$stdout.puts\fP predefined variable in Ruby\&. .sp The following example shows how to add a client named "IBM305RAMAC" and the \fB/clients\fP endpoint, and then return the private key for that user in the console: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ client_desc = { "name" => "IBM305RAMAC", "admin" => false } new_client = api.post("/clients", client_desc) puts new_client["private_key"] .ft P .fi .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec SCRIPT (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-E CODE\fP, \fB\-\-exec CODE\fP A string of code that will be executed. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-p PATH:PATH\fP, \fB\-\-script\-path PATH:PATH\fP A colon\-separated path at which Ruby scripts are located. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp There are three ways to use \fBknife exec\fP to run Ruby script files. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec /path/to/script_file .ft P .fi .UNINDENT .UNINDENT .sp or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqRUBY CODE\(aq .ft P .fi .UNINDENT .UNINDENT .sp or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec RUBY CODE ^D .ft P .fi .UNINDENT .UNINDENT .sp To check the status of knife using a Ruby script named \fBstatus.rb\fP (which looks like): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C printf "%\-5s %\-12s %\-8s %s\en", "Check In", "Name", "Ruby", "Recipes" nodes.all do |n| checkin = Time.at(n[\(aqohai_time\(aq]).strftime("%F %R") rubyver = n[\(aqlanguages\(aq][\(aqruby\(aq][\(aqversion\(aq] recipes = n.run_list.expand(_default).recipes.join(", ") printf "%\-20s %\-12s %\-8s %s\en", checkin, n.name, rubyver, recipes end .ft P .fi .UNINDENT .UNINDENT .sp and is located in a directory named \fBscripts/\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec scripts/status.rb .ft P .fi .UNINDENT .UNINDENT .sp To show the available free memory for all nodes, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqnodes.all {|n| puts "#{n.name} has #{n.memory.total} free memory"}\(aq .ft P .fi .UNINDENT .UNINDENT .sp To list all of the available search indexes, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife exec \-E \(aqputs api.get("search").keys\(aq .ft P .fi .UNINDENT .UNINDENT .sp To query a node for multiple attributes using a Ruby script named \fBsearch_attributes.rb\fP (which looks like): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C % cat scripts/search_attributes.rb query = ARGV[2] attributes = ARGV[3].split(",") puts "Your query: #{query}" puts "Your attributes: #{attributes.join(" ")}" results = {} search(:node, query) do |n| results[n.name] = {} attributes.each {|a| results[n.name][a] = n[a]} end puts results exit 0 .ft P .fi .UNINDENT .UNINDENT .sp enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C % knife exec scripts/search_attributes.rb "hostname:test_system" ipaddress,fqdn .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Your query: hostname:test_system Your attributes: ipaddress fqdn {"test_system.example.com"=>{"ipaddress"=>"10.1.1.200", "fqdn"=>"test_system.example.com"}} .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-index-rebuild.10000644000004100000410000000304112520074675022652 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-INDEX-REBUILD" "1" "Chef 12.0" "" "knife index rebuild" .SH NAME knife-index-rebuild \- The man page for the knife index rebuild subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife index rebuild\fP subcommand is used to rebuild the search indexes for the open source Chef server\&. This operation is destructive and may take some time. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This subcommand ONLY works when run against the open source Chef server version 10.x. This subcommand will NOT run against open source Chef server 11, Enterprise Chef (including hosted Enterprise Chef), or Private Chef\&. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife index rebuild .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-raw.10000644000004100000410000001012212520074675020706 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-RAW" "1" "Chef 12.0" "" "knife raw" .SH NAME knife-raw \- The man page for the knife raw subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife raw\fP subcommand is used to send a REST request to an endpoint in the Chef server API\&. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife raw REQUEST_PATH (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i FILE\fP, \fB\-\-input FILE\fP The name of a file to be used with the \fBPUT\fP or a \fBPOST\fP request. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-m METHOD\fP, \fB\-\-method METHOD\fP The request method: \fBDELETE\fP, \fBGET\fP, \fBPOST\fP, or \fBPUT\fP\&. Default value: \fBGET\fP\&. .TP .B \fB\-\-[no\-]pretty\fP Use \fB\-\-no\-pretty\fP to disable pretty\-print output for JSON\&. Default: \fB\-\-pretty\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To view information about a client: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife raw /clients/ .ft P .fi .UNINDENT .UNINDENT .sp To view information about a node: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife raw /nodes/ .ft P .fi .UNINDENT .UNINDENT .sp To delete a data bag, enter a command similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife raw \-m DELETE /data/foo .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name":"foo", "json_class":"Chef::DataBag", "chef_type":"data_bag" } .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-recipe-list.10000644000004100000410000000327112520074675022344 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-RECIPE-LIST" "1" "Chef 12.0" "" "knife recipe list" .SH NAME knife-recipe-list \- The man page for the knife recipe list subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife recipe list\fP subcommand is used to view all of the recipes that are on a Chef server\&. A regular expression can be used to limit the results to recipes that match a specific pattern. The regular expression must be within quotes and not be surrounded by forward slashes (/). .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife recipe list REGEX .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To view a list of recipes: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife recipe list \(aqcouchdb::*\(aq .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C couchdb::main_monitors couchdb::master couchdb::default couchdb::org_cleanu .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-cookbook-site.10000644000004100000410000004115712520074675022701 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-COOKBOOK-SITE" "1" "Chef 12.0" "" "knife cookbook site" .SH NAME knife-cookbook-site \- The man page for the knife cookbook site subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The Cookbooks Site API is used to provide access to the cookbooks community hosted at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. All of the cookbooks in the community are accessible through a RESTful API located at \fI\%https://supermarket.getchef.com/api/v1/cookbooks\fP by using any of the supported endpoints. In most cases, using knife and the \fBknife cookbook site\fP sub\-command (and any of its arguments) is the recommended method of interacting with these cookbooks, but in some cases, using the Cookbooks Site API directly may make sense. .sp The \fBknife cookbook site\fP subcommand is used to interact with cookbooks that are located at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. A user account is required for any community actions that write data to this site. The following arguments do not require a user account: \fBdownload\fP, \fBsearch\fP, \fBinstall\fP, and \fBlist\fP\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife cookbook site\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH DOWNLOAD .sp The \fBdownload\fP argument is used to download a cookbook from the community website. A cookbook will be downloaded as a tar.gz archive and placed in the current working directory. If a cookbook (or cookbook version) has been deprecated and the \fB\-\-force\fP option is not used, knife will alert the user that the cookbook is deprecated and then will provide the name of the most recent non\-deprecated version of that cookbook. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site download COOKBOOK_NAME [COOKBOOK_VERSION] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBCOOKBOOK_VERSION\fP The version of a cookbook to be downloaded. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, the most recent version of the cookbook will be downloaded. .TP .B \fB\-f FILE\fP, \fB\-\-file FILE\fP The file to which a cookbook download is written. .TP .B \fB\-\-force\fP Use to overwrite an existing directory. .UNINDENT .sp \fBExamples\fP .sp To download the cookbook \fBgetting\-started\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site download getting\-started .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Downloading getting\-started from the cookbooks site at version 0.3.0 to /Users/sdanna/opscodesupport/getting\-started\-0.3.0.tar.gz Cookbook saved: /Users/sdanna/opscodesupport/getting\-started\-0.3.0.tar.gz .ft P .fi .UNINDENT .UNINDENT .SH INSTALL .sp The \fBinstall\fP argument is used to install a cookbook that has been downloaded from the community site to a local git repository . This action uses the git version control system in conjunction with the \fI\%https://supermarket.getchef.com/cookbooks\fP site to install community\-contributed cookbooks to the local chef\-repo\&. Using this argument does the following: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP 1. 3 A new "pristine copy" branch is created in git for tracking the upstream. .IP 2. 3 All existing versions of a cookbook are removed from the branch. .IP 3. 3 The cookbook is downloaded from \fI\%https://supermarket.getchef.com/cookbooks\fP in the tar.gz format. .IP 4. 3 The downloaded cookbook is untarred and its contents are committed to git and a tag is created. .IP 5. 3 The "pristine copy" branch is merged into the master branch. .UNINDENT .UNINDENT .UNINDENT .sp This process allows the upstream cookbook in the master branch to be modified while letting git maintain changes as a separate patch. When an updated upstream version becomes available, those changes can be merged while maintaining any local modifications. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site install COOKBOOK_NAME [COOKBOOK_VERSION] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-b\fP, \fB\-\-use\-current\-branch\fP Use to ensure that the current branch is used. .TP .B \fB\-B BRANCH\fP, \fB\-\-branch BRANCH\fP The name of the default branch. This will default to the master branch. .TP .B \fBCOOKBOOK_VERSION\fP The version of the cookbook to be installed. If a version is not specified, the most recent version of the cookbook will be installed. .TP .B \fB\-D\fP, \fB\-\-skip\-dependencies\fP Use to ensure that all cookbooks to which the installed cookbook has a dependency will not be installed. .TP .B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .UNINDENT .sp \fBExamples\fP .sp To install the cookbook \fBgetting\-started\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site install getting\-started .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Installing getting\-started to /Users/sdanna/opscodesupport/.chef/../cookbooks Checking out the master branch. Creating pristine copy branch chef\-vendor\-getting\-started Downloading getting\-started from the cookbooks site at version 0.3.0 to /Users/sdanna/opscodesupport/.chef/../cookbooks/getting\-started.tar.gz Cookbook saved: /Users/sdanna/opscodesupport/.chef/../cookbooks/getting\-started.tar.gz Removing pre\-existing version. Uncompressing getting\-started version /Users/sdanna/opscodesupport/.chef/../cookbooks. removing downloaded tarball 1 files updated, committing changes Creating tag cookbook\-site\-imported\-getting\-started\-0.3.0 Checking out the master branch. Updating 4d44b5b..b4c32f2 Fast\-forward cookbooks/getting\-started/README.rdoc | 4 +++ cookbooks/getting\-started/attributes/default.rb | 1 + cookbooks/getting\-started/metadata.json | 29 ++++++++++++++++++++ cookbooks/getting\-started/metadata.rb | 6 ++++ cookbooks/getting\-started/recipes/default.rb | 23 +++++++++++++++ .../templates/default/chef\-getting\-started.txt.erb | 5 +++ 6 files changed, 68 insertions(+), 0 deletions(\-) create mode 100644 cookbooks/getting\-started/README.rdoc create mode 100644 cookbooks/getting\-started/attributes/default.rb create mode 100644 cookbooks/getting\-started/metadata.json create mode 100644 cookbooks/getting\-started/metadata.rb create mode 100644 cookbooks/getting\-started/recipes/default.rb create mode 100644 cookbooks/getting\-started/templates/default/chef\-getting\-started.txt.erb Cookbook getting\-started version 0.3.0 successfully installed .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view a list of cookbooks that are currently available at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site list .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To view a list of cookbooks at \fI\%https://supermarket.getchef.com/cookbooks\fP server, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site list .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 1password homesick rabbitmq 7\-zip hostname rabbitmq\-management AmazonEC2Tag hosts rabbitmq_chef R hosts\-awareness rackspaceknife accounts htop radiant ack\-grep hudson rails activemq icinga rails_enterprise ad id3lib redis\-package ad\-likewise iftop redis2 ant iis redmine [...truncated...] .ft P .fi .UNINDENT .UNINDENT .SH SEARCH .sp The \fBsearch\fP argument is used to search for a cookbook at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. A search query is used to return a list of cookbooks at \fI\%https://supermarket.getchef.com/cookbooks\fP and uses the same syntax as the \fBknife search\fP sub\-command. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site search SEARCH_QUERY (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To search for all of the cookbooks that can be used with Apache, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site search apache* .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C apache2: cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/apache2 cookbook_description: Installs and configures apache2 using Debian symlinks with helper definitions cookbook_maintainer: opscode cookbook_name: apache2 instiki: cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/instiki cookbook_description: Installs instiki, a Ruby on Rails wiki server under passenger+Apache2. cookbook_maintainer: jtimberman cookbook_name: instiki kickstart: cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/kickstart cookbook_description: Creates apache2 vhost and serves a kickstart file. cookbook_maintainer: opscode cookbook_name: kickstart [...truncated...] .ft P .fi .UNINDENT .UNINDENT .SH SHARE .sp The \fBshare\fP argument is used to add a cookbook to \fI\%https://supermarket.getchef.com/cookbooks\fP\&. This action will require a user account and a certificate for \fI\%https://supermarket.getchef.com\fP\&. By default, knife will use the user name and API key that is identified in the configuration file used during the upload; otherwise these values must be specified on the command line or in an alternate configuration file. If a cookbook already exists on \fI\%https://supermarket.getchef.com/cookbooks\fP, then only an owner or maintainer of that cookbook can make updates. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site share COOKBOOK_NAME CATEGORY (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBCATEGORY\fP The cookbook category: \fB"Databases"\fP, \fB"Web Servers"\fP, \fB"Process Management"\fP, \fB"Monitoring & Trending"\fP, \fB"Programming Languages"\fP, \fB"Package Management"\fP, \fB"Applications"\fP, \fB"Networking"\fP, \fB"Operating Systems & Virtualization"\fP, \fB"Utilities"\fP, or \fB"Other"\fP\&. .TP .B \fB\-n\fP, \fB\-\-dry\-run\fP Use to take no action and only print out results. Default: \fBfalse\fP\&. .TP .B \fB\-o PATH:PATH\fP, \fB\-\-cookbook\-path PATH:PATH\fP The directory in which cookbooks are created. This can be a colon\-separated path. .UNINDENT .sp \fBExamples\fP .sp To share a cookbook named \fBapache2\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site share "apache2" "Web Servers" .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to view information about a cookbook on \fI\%https://supermarket.getchef.com/cookbooks\fP\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site show COOKBOOK_NAME [COOKBOOK_VERSION] .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBCOOKBOOK_VERSION\fP The version of a cookbook to be shown. If a cookbook has only one version, this option does not need to be specified. If a cookbook has more than one version and this option is not specified, a list of cookbook versions will be returned. .UNINDENT .sp \fBExamples\fP .sp To show the details for a cookbook named \fBhaproxy\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site show haproxy .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C average_rating: category: Networking created_at: 2009\-10\-25T23:51:07Z description: Installs and configures haproxy external_url: latest_version: http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3 maintainer: opscode name: haproxy updated_at: 2011\-06\-30T21:53:25Z versions: http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_2 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_1 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_0 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_1 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_0 http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_7_0 .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH UNSHARE .sp The \fBunshare\fP argument is used to stop the sharing of a cookbook at \fI\%https://supermarket.getchef.com/cookbooks\fP\&. Only the maintainer of a cookbook may perform this action. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site unshare COOKBOOK_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To unshare a cookbook named \fBgetting\-started\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife cookbook site unshare getting\-started .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-role.10000644000004100000410000002154712520074675021073 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-ROLE" "1" "Chef 12.0" "" "knife role" .SH NAME knife-role \- The man page for the knife role subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A role is a way to define certain patterns and processes that exist across nodes in an organization as belonging to a single job function. Each role consists of zero (or more) attributes and a run\-list. Each node can have zero (or more) roles assigned to it. When a role is run against a node, the configuration details of that node are compared against the attributes of the role, and then the contents of that role\(aqs run\-list are applied to the node\(aqs configuration details. When a chef\-client runs, it merges its own attributes and run\-lists with those contained within each assigned role. .sp The \fBknife role\fP subcommand is used to manage the roles that are associated with one or more nodes on a Chef server\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife role\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH BULK DELETE .sp The \fBbulk delete\fP argument is used to delete one or more roles that match a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (/). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role bulk delete REGEX .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp Use a regular expression to define the pattern used to bulk delete roles: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role bulk delete "^[0\-9]{3}$" .ft P .fi .UNINDENT .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to add a role to the Chef server\&. Role data is saved as JSON on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role create ROLE_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-d DESCRIPTION\fP, \fB\-\-description DESCRIPTION\fP The description of the role. This value will populate the description field for the role on the Chef server\&. .UNINDENT .sp \fBExamples\fP .sp To add a role named \fBrole1\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role create role1 .ft P .fi .UNINDENT .UNINDENT .sp In the $EDITOR enter the role data in JSON: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "name": "role1", "default_attributes": { }, "json_class": "Chef::Role", "run_list": [\(aqrecipe[cookbook_name::recipe_name], role[role_name]\(aq ], "description": "", "chef_type": "role", "override_attributes": { } } .ft P .fi .UNINDENT .UNINDENT .sp When finished, save it. .SH DELETE .sp The \fBdelete\fP argument is used to delete a role from the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role delete ROLE_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role delete devops .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH EDIT .sp The \fBedit\fP argument is used to edit role details on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role edit ROLE_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To edit the data for a role named \fBrole1\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role edit role1 .ft P .fi .UNINDENT .UNINDENT .sp Update the role data in JSON: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "name": "role1", "default_attributes": { }, "json_class": "Chef::Role", "run_list": [\(aqrecipe[cookbook_name::recipe_name], role[role_name]\(aq ], "description": "This is the description for the role1 role.", "chef_type": "role", "override_attributes": { } } .ft P .fi .UNINDENT .UNINDENT .sp When finished, save it. .SH FROM FILE .sp The \fBfrom file\fP argument is used to create a role using existing JSON data as a template. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role from file FILE .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To view role details based on the values contained in a JSON file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role from file "path to JSON file" .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view a list of roles that are currently available on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role list .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To view a list of roles on the Chef server and display the URI for each role returned, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role list \-w .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to view the details of a role. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show ROLE_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .UNINDENT .sp \fBExamples\fP .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-search.10000644000004100000410000002124712520074675021374 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SEARCH" "1" "Chef 12.0" "" "knife search" .SH NAME knife-search \- The man page for the knife search subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Search indexes allow queries to be made for any type of data that is indexed by the Chef server, including data bags (and data bag items), environments, nodes, and roles. A defined query syntax is used to support search patterns like exact, wildcard, range, and fuzzy. A search is a full\-text query that can be done from several locations, including from within a recipe, by using the \fBsearch\fP subcommand in knife, the \fBsearch\fP method in the Recipe DSL, and by using the \fB/search\fP or \fB/search/INDEX\fP endpoints in the Chef server API\&. The search engine is based on Apache Solr and is run from the Chef server\&. .sp The \fBknife search\fP subcommand is used run a search query for information that is indexed on a Chef server\&. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search INDEX SEARCH_QUERY .ft P .fi .UNINDENT .UNINDENT .sp where \fBINDEX\fP is one of \fBclient\fP, \fBenvironment\fP, \fBnode\fP, \fBrole\fP, or the name of a data bag and \fBSEARCH_QUERY\fP is the search query syntax for the query that will be executed. .sp \fBINDEX\fP is implied if omitted, and will default to \fBnode\fP\&. For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search \(aq*:*\(aq \-i .ft P .fi .UNINDENT .UNINDENT .sp will return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 8 items found centos\-62\-dev opensuse\-1203 ubuntu\-1304\-dev ubuntu\-1304\-orgtest ubuntu\-1204\-ohai\-test ubuntu\-1304\-ifcfg\-test ohai\-test win2k8\-dev .ft P .fi .UNINDENT .UNINDENT .sp and is the same search as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \(aq*:*" \-i .ft P .fi .UNINDENT .UNINDENT .sp If the \fBSEARCH_QUERY\fP does not contain a colon character (\fB:\fP), then the default query pattern is \fBtags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*\fP, which means the following two search queries are effectively the same: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search ubuntu .ft P .fi .UNINDENT .UNINDENT .sp or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node "tags:*ubuntu* OR roles:*ubuntu* OR fqdn:*ubuntu* (etc.)" .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This sub\-command has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .TP .B \fB\-b ROW\fP, \fB\-\-start ROW\fP The row at which return results will begin. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i\fP, \fB\-\-id\-only\fP Use to show only matching object IDs. .TP .B \fBINDEX\fP The name of the index to be queried: \fBclient\fP, \fBenvironment\fP, \fBnode\fP, \fBrole\fP, or \fBDATA_BAG_NAME\fP\&. Default index: \fBnode\fP\&. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-l\fP, \fB\-\-long\fP Use to display all attributes in the output and to show the output as JSON\&. .TP .B \fB\-m\fP, \fB\-\-medium\fP Use to display normal attributes in the output and to show the output as JSON\&. .TP .B \fB\-o SORT\fP, \fB\-\-sort SORT\fP The order in which search results will be sorted. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-q SEARCH_QUERY\fP, \fB\-\-query SEARCH_QUERY\fP Use to protect search queries that start with a hyphen (\-). A \fB\-q\fP query may be specified as an argument or an option, but not both. .TP .B \fB\-r\fP, \fB\-\-run\-list\fP Use to show only the run\-list. .TP .B \fB\-R INT\fP, \fB\-\-rows INT\fP The number of rows to be returned. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fBSEARCH_QUERY\fP The search query used to identify a a list of items on a Chef server\&. This option uses the same syntax as the \fBsearch\fP sub\-command. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To search for the IDs of all nodes running on the Amazon EC2 platform, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \(aqec2:*\(aq \-i .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 4 items found ip\-0A7CA19F.ec2.internal ip\-0A58CF8E.ec2.internal ip\-0A58E134.ec2.internal ip\-0A7CFFD5.ec2.internal .ft P .fi .UNINDENT .UNINDENT .sp To search for the instance type (flavor) of all nodes running on the Amazon EC2 platform, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \(aqec2:*\(aq \-a ec2.instance_type .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 4 items found ec2.instance_type: m1.large id: ip\-0A7CA19F.ec2.internal ec2.instance_type: m1.large id: ip\-0A58CF8E.ec2.internal ec2.instance_type: m1.large id: ip\-0A58E134.ec2.internal ec2.instance_type: m1.large id: ip\-0A7CFFD5.ec2.internal .ft P .fi .UNINDENT .UNINDENT .sp To search for all nodes running Ubuntu, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \(aqplatform:ubuntu\(aq .ft P .fi .UNINDENT .UNINDENT .sp To search for all nodes running CentOS in the production environment, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \(aqchef_environment:production AND platform:centos\(aq .ft P .fi .UNINDENT .UNINDENT .sp To find a nested attribute, use a pattern similar to the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node \-a . .ft P .fi .UNINDENT .UNINDENT .sp To build a search query to use more than one attribute, use an underscore (\fB_\fP) to separate each attribute. For example, the following query will search for all nodes running a specific version of Ruby: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node "languages_ruby_version:1.9.3" .ft P .fi .UNINDENT .UNINDENT .sp To build a search query that can find a nested attribute: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node name: \-a kernel.machine .ft P .fi .UNINDENT .UNINDENT .sp To test a search query that will be used in a \fBknife ssh\fP command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife search node "role:web NOT name:web03" .ft P .fi .UNINDENT .UNINDENT .sp where the query in the previous example will search all servers that have the \fBweb\fP role, but not on the server named \fBweb03\fP\&. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-list.10000644000004100000410000001202412520074675021073 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-LIST" "1" "Chef 12.0" "" "knife list" .SH NAME knife-list \- The man page for the knife list subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife list\fP subcommand is used to view a list of objects on the Chef server\&. This subcommand works similar to \fBknife cookbook list\fP, \fBknife data bag list\fP, \fBknife environment list\fP, \fBknife node list\fP, and \fBknife role list\fP, but with a single verb (and a single action). .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-1\fP Use to show only one column of results. Default: \fBfalse\fP\&. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP Use to prevent a directory\(aqs children from showing when a directory matches a pattern. Default value: \fBfalse\fP\&. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-f\fP, \fB\-\-flat\fP Use to show a list of file names. Set to \fBfalse\fP to view ls\-like output. Default: \fBfalse\fP\&. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-local\fP Use to return only the contents of the local directory. Default: \fBfalse\fP\&. .TP .B \fB\-p\fP Use to show directories with trailing slashes (/). Default: \fBfalse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-R\fP Use to list directories recursively. Default: \fBfalse\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp For example, to view a list of roles on the Chef server: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list roles/ .ft P .fi .UNINDENT .UNINDENT .sp To view a list of roles and environments on the Chef server: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list roles/ environments/ .ft P .fi .UNINDENT .UNINDENT .sp To view a list of absolutely everything on the Chef server: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list \-R / .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/chef-shell.10000644000004100000410000001164212520074675021045 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "CHEF-SHELL" "1" "Chef 12.0" "" "chef-shell" .SH NAME chef-shell \- The man page for the chef-shell command line tool. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp chef\-shell is a recipe debugging tool that allows the use of breakpoints within recipes. chef\-shell runs as an Interactive Ruby (IRb) session. chef\-shell supports both recipe and attribute file syntax, as well as interactive debugging features. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 chef\-shell is the new name for Shef as of Chef 11.x\&. chef\-shell is backwards compatible and aside from the name change, has the same set of functionality as with previous releases. .UNINDENT .UNINDENT .sp The chef\-shell executable is run as a command\-line tool. .SH MODES .sp chef\-shell is tool that allows knife to be run using an Interactive Ruby (IRb) session. chef\-shell currently supports recipe and attribute file syntax, as well as interactive debugging features. chef\-shell has three run modes: .TS center; |l|l|. _ T{ Mode T} T{ Description T} _ T{ Standalone T} T{ No cookbooks are loaded, and the run list is empty. This mode is the default. T} _ T{ Solo T} T{ chef\-shell acts as a chef\-solo client. It attempts to load the chef\-solo configuration file and JSON attributes. If the JSON attributes set a run list, it will be honored. Cookbooks will be loaded in the same way that chef\-solo loads them. chef\-solo mode is activated with the \fB\-s\fP or \fB\-\-solo\fP command line option, and JSON attributes are specified in the same way as for chef\-solo, with \fB\-j /path/to/chef\-solo.json\fP\&. T} _ T{ Client T} T{ chef\-shell acts as a chef\-client\&. During startup, it reads the chef\-client configuration file and contacts the Chef server to get attributes and cookbooks. The run list will be set in the same way as normal chef\-client runs. chef\-client mode is activated with the \fB\-z\fP or \fB\-\-client\fP options. You can also specify the configuration file with \fB\-c CONFIG\fP and the server URL with \fB\-S SERVER_URL\fP\&. T} _ .TE .SH OPTIONS .sp This command has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C chef\-shell OPTION VALUE OPTION VALUE ... .ft P .fi .UNINDENT .UNINDENT .sp This command has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-standalone\fP Use to run chef\-shell in standalone mode. .TP .B \fB\-c CONFIG\fP, \fB\-\-config CONFIG\fP The configuration file to use. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-j PATH\fP, \fB\-\-json\-attributes PATH\fP The path to a file that contains JSON data. .sp Use this option to define a \fBrun_list\fP object. For example, a JSON file similar to: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C "run_list": [ "recipe[base]", "recipe[foo]", "recipe[bar]", "role[webserver]" ], .ft P .fi .UNINDENT .UNINDENT .sp may be used by running \fBchef\-client \-j path/to/file.json\fP\&. .sp In certain situations this option may be used to update \fBnormal\fP attributes. .sp \fBWARNING:\fP .INDENT 7.0 .INDENT 3.5 Any other attribute type that is contained in this JSON file will be treated as a \fBnormal\fP attribute. For example, attempting to update \fBoverride\fP attributes using the \fB\-j\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } .ft P .fi .UNINDENT .UNINDENT .sp will result in a node object similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name": "maybe\-dev\-99", "normal": { "name": "dev\-99", "description": "Install some stuff", "override_attributes": { "apptastic": { "enable_apptastic": "false", "apptastic_tier_name": "dev\-99.bomb.com" } } } } .ft P .fi .UNINDENT .UNINDENT .UNINDENT .UNINDENT .TP .B \fB\-l LEVEL\fP, \fB\-\-log\-level LEVEL\fP The level of logging that will be stored in a log file. .TP .B \fB\-s\fP, \fB\-\-solo\fP Use to run chef\-shell in chef\-solo mode. .TP .B \fB\-S CHEF_SERVER_URL\fP, \fB\-\-server CHEF_SERVER_URL\fP The URL for the Chef server\&. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-z\fP, \fB\-\-client\fP Use to run chef\-shell in chef\-client mode. .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-delete.10000644000004100000410000001050712520074675021366 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-DELETE" "1" "Chef 12.0" "" "knife delete" .SH NAME knife-delete \- The man page for the knife delete subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife delete\fP subcommand is used to delete an object from a Chef server\&. This subcommand works similar to \fBknife cookbook delete\fP, \fBknife data bag delete\fP, \fBknife environment delete\fP, \fBknife node delete\fP, and \fBknife role delete\fP, but with a single verb (and a single action). .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife delete [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-\-both\fP Use to delete both local and remote copies of an object. Default: \fBfalse\fP\&. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-local\fP Use to delete only the local copy of an object. (A remote copy will not be deleted.) Default: \fBfalse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-r\fP, \fB\-\-[no\-]recurse\fP Use \fB\-\-recurse\fP to delete directories recursively. Default: \fB\-\-no\-recurse\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-configure.10000644000004100000410000001030612520074675022102 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-CONFIGURE" "1" "Chef 12.0" "" "knife configure" .SH NAME knife-configure \- The man page for the knife configure subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife configure\fP subcommand is used to create the knife.rb and client.rb files so that they can be distributed to workstations and nodes. .sp \fBSyntax\fP .sp This subcommand has the following syntax when creating a knife.rb file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife configure (options) .ft P .fi .UNINDENT .UNINDENT .sp and the following syntax when creating a client.rb file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife configure client DIRECTORY .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-\-admin\-client\-key PATH\fP The path to the private key used by the client, typically a file named \fBadmin.pem\fP\&. .TP .B \fB\-\-admin\-client\-name NAME\fP The name of the client, typically the name of the admin client. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i\fP, \fB\-\-initial\fP Use to create a API client, typically an administrator client on a freshly\-installed Chef server\&. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-r REPO\fP, \fB\-\-repository REPO\fP The path to the chef\-repo\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-\-validation\-client\-name NAME\fP The name of the validation client. .TP .B \fB\-\-validation\-key PATH\fP The path to the validation key used by the client, typically a file named \fBvalidation.pem\fP\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife configure .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife configure client \(aq/directory\(aq .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-serve.10000644000004100000410000000700012520074675021242 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SERVE" "1" "Chef 12.0" "" "knife serve" .SH NAME knife-serve \- The man page for the knife serve subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife serve\fP subcommand is used to run a persistent chef\-zero against the local chef\-repo\&. (chef\-zero is a lightweight Chef server that runs in\-memory on the local machine.) This is the same as running the chef\-client executable with the \fB\-\-local\-mode\fP option. The \fBchef_repo_path\fP is located automatically and the Chef server will bind to the first available port between \fB8889\fP and \fB9999\fP\&. \fBknife serve\fP will print the URL for the local Chef server, so that it may be added to the knife.rb file. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife serve (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-diff.10000644000004100000410000001577612520074675021051 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-DIFF" "1" "Chef 12.0" "" "knife diff" .SH NAME knife-diff \- The man page for the knife diff subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife diff\fP subcommand is used to compare the differences between files and directories on the Chef server and in the chef\-repo\&. For example, to compare files on the Chef server prior to an uploading or downloading files using the \fBknife download\fP and \fBknife upload\fP subcommands, or to ensure that certain files in multiple production environments are the same. This subcommand is similar to the \fBgit diff\fP command that can be used to diff what is in the chef\-repo with what is synced to a git repository. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife diff [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-cookbook\-version VERSION\fP The version of a cookbook to be downloaded. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-\-diff\-filter=[(A|D|M|T)...[*]]\fP Use to select only files that have been added (\fBA\fP), deleted (\fBD\fP), modified (\fBM\fP), and/or have had their type changed (\fBT\fP). Any combination of filter characters may be used, including no filter characters. Use \fB*\fP to select all paths if a file matches other criteria in the comparison. Default value: \fBnil\fP\&. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-name\-only\fP Use to show only the names of modified files. .TP .B \fB\-\-name\-status\fP Use to show only the names of files with a status of \fBAdded\fP, \fBDeleted\fP, \fBModified\fP, or \fBType Changed\fP\&. .TP .B \fB\-\-no\-recurse\fP Use \fB\-\-no\-recurse\fP to disable listing a directory recursively. Default: \fB\-\-recurse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBknife.rb File Settings\fP .sp In addition to the default settings in a knife.rb file, there are other subcommand\-specific settings that can be added. When a subcommand is run, knife will use: .INDENT 0.0 .IP 1. 3 A value passed via the command\-line .IP 2. 3 A value contained in the knife.rb file .IP 3. 3 The default value .UNINDENT .sp A value passed via the command line will override a value in the knife.rb file; a value in a knife.rb file will override a default value. .sp The following \fBknife diff\fP settings can be added to the knife.rb file: .INDENT 0.0 .TP .B \fBknife[:chef_repo_path]\fP Use to add the \fB\-\-chef\-repo\-path\fP option. .TP .B \fBknife[:concurrency]\fP Use to add the \fB\-\-concurrency\fP option. .TP .B \fBknife[:name_only]\fP Use to add the \fB\-\-name\-only\fP option. .TP .B \fBknife[:name_status]\fP Use to add the \fB\-\-name\-status\fP option. .TP .B \fBknife[:recurse]\fP Use to add the \fB\-\-recurse\fP option. .TP .B \fBknife[:repo_mode]\fP Use to add the \fB\-\-repo\-mode\fP option. .UNINDENT .sp \fBExamples\fP .sp To compare the \fBbase.json\fP role to a \fBwebserver.json\fP role, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife diff roles/base.json roles/webserver.json .ft P .fi .UNINDENT .UNINDENT .sp To compare the differences between the local chef\-repo and the files that are on the Chef server, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife diff .ft P .fi .UNINDENT .UNINDENT .sp To diff a node named \fBnode\-lb\fP and then only return files that have been added, deleted, modified, or changed, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife diff \-\-name\-status node\-lb .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C node\-lb/recipes/eip.rb node\-lb/recipes/heartbeat\-int.rb node\-lb/templates/default/corpsite.conf.erb node\-lb/files/default/wildcard.node.com.crt node\-lb/files/default/wildcard.node.com.crt\-2009 node\-lb/files/default/wildcard.node.com.key node\-lb/.gitignore node\-lb/Rakefile .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-bootstrap.10000644000004100000410000001756212520074675022151 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-BOOTSTRAP" "1" "Chef 12.0" "" "knife bootstrap" .SH NAME knife-bootstrap \- The man page for the knife bootstrap subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A bootstrap is a process that installs the chef\-client on a target system so that it can run as a chef\-client and communicate with a Chef server\&. .sp The \fBknife bootstrap\fP subcommand is used to run a bootstrap operation that installs the chef\-client on the target system. The bootstrap operation must specify the IP address or FQDN of the target system. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife bootstrap FQDN_or_IP_ADDRESS (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-A\fP, \fB\-\-forward\-agent\fP Use to enable SSH agent forwarding. .TP .B \fB\-\-bootstrap\-curl\-options OPTIONS\fP Use to specify arbitrary options to be added to the bootstrap command when using cURL\&. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&. .TP .B \fB\-\-bootstrap\-install\-command COMMAND\fP Use to execute a custom installation command sequence for the chef\-client\&. This option may not be used in the same command with \fB\-\-bootstrap\-curl\-options\fP, \fB\-\-bootstrap\-install\-sh\fP, or \fB\-\-bootstrap\-wget\-options\fP\&. .TP .B \fB\-\-bootstrap\-install\-sh URL\fP Use to fetch and execute an installation script at the specified URL. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&. .TP .B \fB\-\-bootstrap\-no\-proxy NO_PROXY_URL_or_IP\fP A URL or IP address that specifies a location that should not be proxied. .sp \fBNOTE:\fP .INDENT 7.0 .INDENT 3.5 This option is used internally by Chef to help verify bootstrap operations during testing and should never be used during an actual bootstrap operation. .UNINDENT .UNINDENT .TP .B \fB\-\-bootstrap\-proxy PROXY_URL\fP The proxy server for the node that is the target of a bootstrap operation. .TP .B \fB\-\-bootstrap\-version VERSION\fP The version of the chef\-client to install. .TP .B \fB\-\-bootstrap\-wget\-options OPTIONS\fP Use to specify arbitrary options to be added to the bootstrap command when using GNU Wget\&. This option may not be used in the same command with \fB\-\-bootstrap\-install\-command\fP\&. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation. .TP .B \fB\-\-hint HINT_NAME[=HINT_FILE]\fP Use to specify an Ohai hint to be set on the target node. .sp Ohai hints are used to tell Ohai something about the system that it is running on that it would not be able to discover itself. An Ohai hint exists if a JSON file exists in the hint directory with the same name as the hint. For example, calling \fBhint?(\(aqantartica\(aq)\fP in an Ohai plugin would return an empty hash if the file \fBantartica.json\fP existed in the hints directory, and return nil if the file does not exist. .sp If the hint file contains JSON content, it will be returned as a hash from the call to \fBhint?\fP\&. .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C { "snow": true, "penguins": "many" } .ft P .fi .UNINDENT .UNINDENT .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C arctic_hint = hint?(\(aqantartica\(aq) if arctic_hint[\(aqsnow\(aq] "There are #{arctic_hint[\(aqpenguins\(aq]} penguins here." else "There is no snow here, and penguins like snow." end .ft P .fi .UNINDENT .UNINDENT .sp The default directory in which hint files are located is \fB/etc/chef/ohai/hints/\fP\&. Use the \fBOhai::Config[:hints_path]\fP setting in the client.rb file to customize this location. .sp \fBHINT_FILE\fP is the name of the JSON file. \fBHINT_NAME\fP is the name of a hint in a JSON file. Use multiple \fB\-\-hint\fP options to specify multiple hints. .TP .B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTITY_FILE\fP The SSH identity file used for authentication. Key\-based authentication is recommended. .TP .B \fB\-j JSON_ATTRIBS\fP, \fB\-\-json\-attributes JSON_ATTRIBS\fP A JSON string that is added to the first run of a chef\-client\&. .TP .B \fB\-N NAME\fP, \fB\-\-node\-name NAME\fP The name of the node. .TP .B \fB\-\-[no\-]host\-key\-verify\fP Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&. .TP .B \fB\-\-[no\-]node\-verify\-api\-cert\fP Use \fBverify_api_cert\fP to only do SSL validation of the Chef server connection; may be needed if the chef\-client needs to talk to other services that have broken SSL certificates. If this option is not specified, the setting for \fBverify_api_cert\fP in the configuration file is applied. .TP .B \fB\-\-node\-ssl\-verify\-mode PEER_OR_NONE\fP The verify mode for HTTPS requests. .sp Use \fB:verify_none\fP to do no validation of SSL certificates. .sp Use \fB:verify_peer\fP to do validation of all SSL certificates, including the Chef server connections, S3 connections, and any HTTPS \fBremote_file\fP resource URLs used in the chef\-client run. This is the recommended setting. .sp If this option is not specified, the setting for \fBssl_verify_mode\fP in the configuration file is applied. .TP .B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP The SSH port. .TP .B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password. .TP .B \fB\-\-prerelease\fP Use to install pre\-release gems. .TP .B \fB\-r RUN_LIST\fP, \fB\-\-run\-list RUN_LIST\fP A comma\-separated list of roles and/or recipes to be applied. .TP .B \fB\-\-secret SECRET\fP The encryption key that is used for values contained within a data bag item. .TP .B \fB\-\-secret\-file FILE\fP The path to the file that contains the encryption key. .TP .B \fB\-\-sudo\fP Use to execute a bootstrap operation with sudo\&. .TP .B \fB\-t TEMPLATE\fP, \fB\-\-bootstrap\-template TEMPLATE\fP Use to specify the bootstrap template to use. This may specify the name of a bootstrap template\-\-\-\fBchef\-full\fP, for example\-\-\-or it may specify the full path to an Embedded Ruby (ERB) template that defines a custom bootstrap. Default value: \fBchef\-full\fP, which installs the chef\-client using the omnibus installer on all supported platforms. .TP .B \fB\-\-use\-sudo\-password\fP Use to perform a bootstrap operation with sudo; specify the password with the \fB\-P\fP (or \fB\-\-ssh\-password\fP) option. .TP .B \fB\-V \-V\fP Use to run the initial chef\-client run at the \fBdebug\fP log\-level (e.g. \fBchef\-client \-l debug\fP). .TP .B \fB\-x USERNAME\fP, \fB\-\-ssh\-user USERNAME\fP The SSH user name. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife bootstrap 192.168.1.1 \-x username \-P PASSWORD \-\-sudo .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife bootstrap 192.168.1.1 \-x username \-i ~/.ssh/id_rsa \-\-sudo .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-tag.10000644000004100000410000001112212520074675020671 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-TAG" "1" "Chef 12.0" "" "knife tag" .SH NAME knife-tag \- The man page for the knife tag subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A tag is a custom description that is applied to a node. A tag, once applied, can be helpful when managing nodes using knife or when building recipes by providing alternate methods of grouping similar types of information. .sp The \fBknife tag\fP subcommand is used to apply tags to nodes on a Chef server\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife tag\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to add one or more tags to a node. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife tag create NODE_NAME [TAG...] .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To create tags named \fBseattle\fP, \fBportland\fP, and \fBvancouver\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife tag create node seattle portland vancouver .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete one or more tags from a node. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife tag delete NODE_NAME [TAG...] .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To delete tags named \fBdenver\fP and \fBphoenix\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife tag delete node denver phoenix .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH LIST .sp The \fBlist\fP argument is used to list all of the tags that have been applied to a node. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife tag list [NODE_NAME...] .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-download.10000644000004100000410000001643212520074675021736 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-DOWNLOAD" "1" "Chef 12.0" "" "knife download" .SH NAME knife-download \- The man page for the knife download subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife download\fP subcommand is used to download roles, cookbooks, environments, nodes, and data bags from the Chef server to the current working directory. It can be used to back up data on the Chef server, inspect the state of one or more files, or to extract out\-of\-process changes users may have made to files on the Chef server, such as if a user made a change that bypassed version source control. This subcommand is often used in conjunction with \fBknife diff\fP, which can be used to see exactly what changes will be downloaded, and then \fBknife upload\fP, which does the opposite of \fBknife download\fP\&. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-\-cookbook\-version VERSION\fP The version of a cookbook to be downloaded. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-\-[no\-]diff\fP Use to download only new and modified files. Set to \fBfalse\fP to download all files. Default: \fB\-\-diff\fP\&. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-\-[no\-]force\fP Use \fB\-\-force\fP to download files even when the file on the hard drive is identical to the object on the server (role, cookbook, etc.). By default, files are compared to see if they have equivalent content, and local files are only overwritten if they are different. Default: \fB\-\-no\-force\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-n\fP, \fB\-\-dry\-run\fP Use to take no action and only print out results. Default: \fBfalse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-[no\-]purge\fP Use \fB\-\-purge\fP to delete local files and directories that do not exist on the Chef server\&. By default, if a role, cookbook, etc. does not exist on the Chef server, the local file for said role will be left alone and NOT deleted. Default: \fB\-\-no\-purge\fP\&. .TP .B \fB\-\-[no\-]recurse\fP Use \fB\-\-no\-recurse\fP to disable downloading a directory recursively. Default: \fB\-\-recurse\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To download the entire chef\-repo from the Chef server, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download / .ft P .fi .UNINDENT .UNINDENT .sp To download the \fBcookbooks/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download cookbooks .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download /cookbooks .ft P .fi .UNINDENT .UNINDENT .sp To download the \fBenvironments/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download environments .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download /environments .ft P .fi .UNINDENT .UNINDENT .sp To download an environment named "production" from the Chef server, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download environments/production.json .ft P .fi .UNINDENT .UNINDENT .sp or from the \fBenvironments/\fP directory, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download production.json .ft P .fi .UNINDENT .UNINDENT .sp To download the \fBroles/\fP directory from the Chef server, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download roles .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download /roles .ft P .fi .UNINDENT .UNINDENT .sp To download all cookbooks that start with "apache" and belong to the "webserver" role, browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife download cookbooks/apache\e* roles/webserver.json .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-client.10000644000004100000410000002601212520074675021400 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-CLIENT" "1" "Chef 12.0" "" "knife client" .SH NAME knife-client \- The man page for the knife client subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp Every request made by the chef\-client to the Chef server must be an authenticated request using the Chef server API and a private key. When the chef\-client makes a request to the Chef server, the chef\-client authenticates each request using a private key located in \fB/etc/chef/client.pem\fP\&. .sp However, during the first chef\-client run, this private key does not exist. Instead, the chef\-client will attempt to use the private key assigned to the chef\-validator, located in \fB/etc/chef/validation.pem\fP\&. (If, for any reason, the chef\-validator is unable to make an authenticated request to the Chef server, the initial chef\-client run will fail.) .sp During the initial chef\-client run, the chef\-client will register with the Chef server using the private key assigned to the chef\-validator, after which the chef\-client will obtain a \fBclient.pem\fP private key for all future authentication requests to the Chef server\&. .sp After the initial chef\-client run has completed successfully, the chef\-validator is no longer required and may be deleted from the node. Use the \fBdelete_validation\fP recipe found in the \fBchef\-client\fP cookbook (\fI\%https://github.com/opscode\-cookbooks/chef\-client\fP) to remove the chef\-validator\&. .sp The \fBknife client\fP subcommand is used to manage an API client list and their associated RSA public key\-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef\-client and knife\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife client\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH BULK DELETE .sp The \fBbulk delete\fP argument is used to delete any API client that matches a pattern defined by a regular expression. The regular expression must be within quotes and not be surrounded by forward slashes (\fB/\fP). .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client bulk delete REGEX .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .SH CREATE .sp The \fBcreate\fP argument is used to create a new API client\&. This process will generate an RSA key pair for the named API client\&. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file. .INDENT 0.0 .IP \(bu 2 For the chef\-client, the private key should be copied to the system as \fB/etc/chef/client.pem\fP\&. .IP \(bu 2 For knife, the private key is typically copied to \fB~/.chef/client_name.pem\fP and referenced in the knife.rb configuration file. .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client create CLIENT_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-admin\fP Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef\&. .TP .B \fB\-f FILE\fP, \fB\-\-file FILE\fP Use to save a private key to the specified file name. .TP .B \fB\-\-validator\fP Use to create the client as the chef\-validator\&. Default value: \fBtrue\fP\&. .UNINDENT .sp \fBExamples\fP .sp To create a chef\-client that can access the Chef server API as an administrator\-\-\-sometimes referred to as an "API chef\-client"\-\-\-with the name "exampleorg" and save its private key to a file, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client create exampleorg \-a \-f "/etc/chef/client.pem" .ft P .fi .UNINDENT .UNINDENT .sp When running the \fBcreate\fP argument on Enterprise Chef, be sure to omit the \fB\-a\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client create exampleorg \-f "/etc/chef/client.pem" .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete a registered API client\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client delete CLIENT_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To delete a client with the name "client_foo", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client delete client_foo .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH EDIT .sp The \fBedit\fP argument is used to edit the details of a registered API client\&. When this argument is run, knife will open $EDITOR to enable editing of the \fBadmin\fP attribute. (None of the other attributes should be changed using this argument.) When finished, knife will update the Chef server with those changes. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client edit CLIENT_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To edit a client with the name "exampleorg", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client edit exampleorg .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view a list of registered API client\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client list (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To verify the API client list for the Chef server, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client list .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C exampleorg i\-12345678 rs\-123456 .ft P .fi .UNINDENT .UNINDENT .sp To verify that an API client can authenticate to the Chef server correctly, try getting a list of clients using \fB\-u\fP and \fB\-k\fP options to specify its name and private key: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client list \-u ORGNAME \-k .chef/ORGNAME.pem .ft P .fi .UNINDENT .UNINDENT .SH REREGISTER .sp The \fBreregister\fP argument is used to regenerate an RSA key pair for an API client\&. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server\&. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client reregister CLIENT_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP Use to save a private key to the specified file name. .UNINDENT .sp \fBExamples\fP .sp To regenerate the RSA key pair for a client named "testclient" and save it to a file named "rsa_key", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client regenerate testclient \-f rsa_key .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to show the details of an API client\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client show CLIENT_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .UNINDENT .sp \fBExamples\fP .sp To view a client named "testclient", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife client show testclient .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C admin: false chef_type: client json_class: Chef::ApiClient name: testclient public_key: .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-data-bag.10000644000004100000410000003240012520074675021560 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-DATA-BAG" "1" "Chef 12.0" "" "knife data bag" .SH NAME knife-data-bag \- The man page for the knife data bag subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A data bag is a global variable that is stored as JSON data and is accessible from a Chef server\&. A data bag is indexed for searching and can be loaded by a recipe or accessed during a search. .sp A data bag item may be encrypted using \fI\%shared secret encryption\fP\&. This allows each data bag item to store confidential information (such as a database password) or to be managed in a source control system (without plain\-text data appearing in revision history). Each data bag item may be encrypted individually; if a data bag contains multiple encrypted data bag items, these data bag items are not required to share the same encryption keys. .sp The \fBknife data bag\fP subcommand is used to manage arbitrary stores of globally available JSON data. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife data bag\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to add a data bag to the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag create DATA_BAG_NAME [DATA_BAG_ITEM] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBDATA_BAG_ITEM\fP The name of a specific item within a data bag. .TP .B \fB\-\-secret SECRET\fP The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file. .TP .B \fB\-\-secret\-file FILE\fP The path to the file that contains the encryption key. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both. .UNINDENT .UNINDENT .sp \fBExamples\fP .sp To create a data bag named "admins", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag create admins .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Created data_bag[admins] .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete a data bag or a data bag item from a Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag delete DATA_BAG_NAME [DATA_BAG_ITEM] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBDATA_BAG_ITEM\fP The name of a specific item within a data bag. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag delete data_bag_name .ft P .fi .UNINDENT .UNINDENT .sp To delete an item named "charlie", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag delete admins charlie .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH EDIT .sp The \fBedit\fP argument is used to edit the data contained in a data bag. If encryption is being used, the data bag will be decrypted, the data will be made available in the $EDITOR, and then encrypted again before saving it to the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag edit DATA_BAG_NAME [DATA_BAG_ITEM] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBDATA_BAG_ITEM\fP The name of a specific item within a data bag. .TP .B \fB\-\-secret SECRET\fP The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file. .TP .B \fB\-\-secret\-file FILE\fP The path to the file that contains the encryption key. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both. .UNINDENT .UNINDENT .sp \fBExamples\fP .sp To edit the contents of a data bag, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag edit dogs tibetanspaniel .ft P .fi .UNINDENT .UNINDENT .sp where \fBdogs\fP is the name of the data bag and \fBtibetanspaniel\fP is the name of the data bag item. This will return something similar to the following in the knife editor: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "name":"data_bag_item_dogs_tibetanspaniel", "json_class":"Chef::DataBagItem", "chef_type":"data_bag_item", "data_bag":"dogs", "raw_data": { "description":"small dog that likes to sit in windows", "id":"tibetanspaniel" } } .ft P .fi .UNINDENT .UNINDENT .sp Make the necessary changes to the key\-value pairs under \fBraw_data\fP and save them. .sp To edit an item named "charlie" that is contained in a data bag named "admins", enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag edit admins charlie .ft P .fi .UNINDENT .UNINDENT .sp to open the $EDITOR\&. Once opened, you can update the data before saving it to the Chef server\&. For example, by changing: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "id": "charlie" } .ft P .fi .UNINDENT .UNINDENT .sp to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "id": "charlie", "uid": 1005, "gid": "ops", "shell": "/bin/zsh", "comment": "Crazy Charlie" } .ft P .fi .UNINDENT .UNINDENT .SH FROM FILE .sp The \fBfrom file\fP argument is used to: .INDENT 0.0 .IP \(bu 2 Add a data bag item to a data bag .IP \(bu 2 Update the contents of an existing data bag item .UNINDENT .sp The data bag itself must already exist on the Chef server and must be specified as part of the command. The contents of the data bag item are specified using a JSON file. This JSON file may be located at a relative or absolute path; its location must be specified as part of the command. The JSON file that defines the contents of the data bag item must at least contain the name of the data bag item\-\-\-\fB"id": "name"\fP\&. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 A chef\-client must be version 11.6 (or higher) when using the \fBknife data bag from file\fP argument with the Enterprise Chef or Open Source Chef version 11 servers. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag from file DATA_BAG_NAME_or_PATH .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to upload all data bags found at the specified path. .TP .B \fB\-\-secret SECRET\fP The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file. .TP .B \fB\-\-secret\-file FILE\fP The path to the file that contains the encryption key. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both. .UNINDENT .UNINDENT .sp \fBExamples\fP .sp To create a data bag on the Chef server from a file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag from file "path to JSON file" .ft P .fi .UNINDENT .UNINDENT .sp To create a data bag named "devops_data" that contains encrypted data, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag from file devops_data \-\-secret\-file "path to decryption file" .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to view a list of data bags that are currently available on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag list .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag list .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to view the contents of a data bag. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show DATA_BAG_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fBDATA_BAG_ITEM\fP The name of a specific item within a data bag. .TP .B \fB\-\-secret SECRET\fP The encryption key that is used for values contained within a data bag item. If \fBsecret\fP is not specified, the chef\-client will look for a secret at the path specified by the \fBencrypted_data_bag_secret\fP setting in the client.rb file. .TP .B \fB\-\-secret\-file FILE\fP The path to the file that contains the encryption key. .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 For encrypted data bag items, use \fIeither\fP \fB\-\-secret\fP or \fB\-\-secret\-file\fP, not both. .UNINDENT .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show admins .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C charlie .ft P .fi .UNINDENT .UNINDENT .sp To show the contents of a specific item within data bag, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show admins charlie .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C comment: Crazy Charlie gid: ops id: charlie shell: /bin/zsh uid: 1005 .ft P .fi .UNINDENT .UNINDENT .sp To show the contents of a data bag named \fBpasswords\fP with an item that contains encrypted data named \fBmysql\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show passwords mysql .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "id": "mysql", "pass": "trywgFA6R70NO28PNhMpGhEvKBZuxouemnbnAUQsUyo=\en", "user": "e/p+8WJYVHY9fHcEgAAReg==\en" } .ft P .fi .UNINDENT .UNINDENT .sp To show the decrypted contents of the same data bag, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show \-\-secret\-file /path/to/decryption/file passwords mysql .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ## sample: { "id": "mysql", "pass": "thesecret123", "user": "fred" } .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag show admins \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-show.10000644000004100000410000001105412520074675021102 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SHOW" "1" "Chef 12.0" "" "knife show" .SH NAME knife-show \- The man page for the knife show subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife show\fP subcommand is used to view the details of one (or more) objects on the Chef server\&. This subcommand works similar to \fBknife cookbook show\fP, \fBknife data bag show\fP, \fBknife environment show\fP, \fBknife node show\fP, and \fBknife role show\fP, but with a single verb (and a single action). .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife show [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-local\fP Use to show local files instead of remote files. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To show all cookbooks in the \fBcookbooks/\fP directory: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife show cookbooks/ .ft P .fi .UNINDENT .UNINDENT .sp or, (if already in the \fBcookbooks/\fP directory in the local chef\-repo): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife show .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife show roles/ environments/ .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-ssl-fetch.10000644000004100000410000001523312520074675022015 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SSL-FETCH" "1" "Chef 12.0" "" "knife ssl fetch" .SH NAME knife-ssl-fetch \- The man page for the knife ssl fetch subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife ssl fetch\fP subcommand is used to copy SSL certificates from an HTTPS server to the \fBtrusted_certs_dir\fP directory that is used by knife and the chef\-client to store trusted SSL certificates. When these certificates match the hostname of the remote server, running \fBknife ssl fetch\fP is the only step required to verify a remote server that is accessed by either knife or the chef\-client\&. .sp \fBWARNING:\fP .INDENT 0.0 .INDENT 3.5 It is the user\(aqs responsibility to verify the authenticity of every SSL certificate before downloading it to the \fBtrusted_certs_dir\fP directory. knife will use any certificate in that directory as if it is a 100% trusted and authentic SSL certificate. knife will not be able to determine if any certificate in this directory has been tampered with, is forged, malicious, or otherwise harmful. Therefore it is essential that users take the proper steps before downloading certificates into this directory. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl fetch URI_FOR_HTTPS_SERVER .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname. .TP .B \fB\-A\fP, \fB\-\-forward\-agent\fP Use to enable SSH agent forwarding. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP The number of allowed concurrent connections. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP The SSH identity file used for authentication. Key\-based authentication is recommended. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-m\fP, \fB\-\-manual\-list\fP Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP .TP .B \fB\-\-[no\-]host\-key\-verify\fP Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&. .TP .B \fBOTHER\fP The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.) .TP .B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP The SSH port. .TP .B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fBSEARCH_QUERY\fP The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command. .TP .B \fBSSH_COMMAND\fP The command that will be run against the results of a search query. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP The SSH user name. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp The following examples show how to use this knife subcommand: .sp \fBFetch the SSL certificates used by Knife from the Chef server\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl fetch .ft P .fi .UNINDENT .UNINDENT .sp \fBFetch the SSL certificates used by the chef\-client from the Chef server\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl fetch \-c /etc/chef/client.rb .ft P .fi .UNINDENT .UNINDENT .sp \fBFetch SSL certificates from a URL or URI\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl fetch URL_or_URI .ft P .fi .UNINDENT .UNINDENT .sp for example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssl fetch https://www.getchef.com .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-environment.10000644000004100000410000002562412520074675022476 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-ENVIRONMENT" "1" "Chef 12.0" "" "knife environment" .SH NAME knife-environment \- The man page for the knife environment subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp An environment is a way to map an organization\(aqs real\-life workflow to what can be configured and managed when using Chef server\&. Every organization begins with a single environment called the \fB_default\fP environment, which cannot be modified (or deleted). Additional environments can be created to reflect each organization\(aqs patterns and workflow. For example, creating \fBproduction\fP, \fBstaging\fP, \fBtesting\fP, and \fBdevelopment\fP environments. Generally, an environment is also associated with one (or more) cookbook versions. .sp The \fBknife environment\fP subcommand is used to manage environments within a single organization on the Chef server\&. .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife environment\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH COMPARE .sp The \fBcompare\fP argument is used to compare the cookbook version constraints that are set on one (or more) environments. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment compare [ENVIRONMENT_NAME...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to upload all environments found at the specified path. .TP .B \fB\-m\fP, \fB\-\-mismatch\fP Use to show only matching versions. .UNINDENT .sp \fBExample\fP .sp To compare cookbook versions for a single environment: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment compare development .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C development apache 2.3.1 windows 4.1.2 .ft P .fi .UNINDENT .UNINDENT .sp To compare cookbook versions for multiple environments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment compare development staging .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C development staging apache 2.3.1 1.2.2 windows 4.1.2 1.0.0 postgresql 1.0.0 1.0.0 .ft P .fi .UNINDENT .UNINDENT .sp To compare all cookbook versions for all environments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment compare \-\-all .ft P .fi .UNINDENT .UNINDENT .sp to return something similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C staging development ulimit latest latest redisio latest latest journly latest latest aws latest latest test latest latest unicorn latest latest sensu latest latest runit latest latest templater latest latest powershell latest latest openssl latest latest rbenv latest latest rabbitmq latest latest postgresql latest latest mysql latest latest ohai latest latest git latest latest erlang latest latest ssh_known_hosts latest latest nginx latest latest database latest latest yum latest latest xfs latest latest apt latest latest dmg latest latest chef_handler latest latest windows 1.0.0 4.1.2 .ft P .fi .UNINDENT .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to add an environment object to the Chef server\&. When this argument is run, knife will open $EDITOR to enable editing of the \fBENVIRONMENT\fP description field (unless a description is specified as part of the command). When finished, knife will add the environment to the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment create ENVIRONMENT_NAME \-d DESCRIPTION .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-d DESCRIPTION\fP, \fB\-\-description DESCRIPTION\fP The description of the environment. This value will populate the description field for the environment on the Chef server\&. .UNINDENT .sp \fBExamples\fP .sp To create an environment named \fBdev\fP with a description of \fBThe development environment.\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment create dev \-d "The development environment." .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete an environment from a Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment delete ENVIRONMENT_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To delete an environment named \fBdev\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment delete dev .ft P .fi .UNINDENT .UNINDENT .sp Type \fBY\fP to confirm a deletion. .SH EDIT .sp The \fBedit\fP argument is used to edit the attributes of an environment. When this argument is run, knife will open $EDITOR to enable editing of \fBENVIRONMENT\fP attributes. When finished, knife will update the Chef server with those changes. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment edit ENVIRONMENT_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To edit an environment named \fBdevops\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment edit devops .ft P .fi .UNINDENT .UNINDENT .SH FROM FILE .sp The \fBfrom file\fP argument is used to add or update an environment using a JSON or Ruby DSL description. It must be run with the \fBcreate\fP or \fBedit\fP arguments. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment [create | edit] from file FILE (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-all\fP Use to upload all environments found at the specified path. .UNINDENT .sp \fBExamples\fP .sp To add an environment using data contained in a JSON file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment create devops from file "path to JSON file" .ft P .fi .UNINDENT .UNINDENT .sp or: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment edit devops from file "path to JSON file" .ft P .fi .UNINDENT .UNINDENT .SH LIST .sp The \fBlist\fP argument is used to list all of the environments that are currently available on the Chef server\&. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment list \-w .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp To view a list of environments: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment list \-w .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to display information about the specified environment. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment show ENVIRONMENT_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp To view information about the \fBdev\fP environment enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife environment show dev .ft P .fi .UNINDENT .UNINDENT .sp to return: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C % knife environment show dev chef_type: environment cookbook_versions: default_attributes: description: json_class: Chef::Environment name: dev override_attributes: \e\e \e\e \e\e \e\e .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife role show devops \-F json .ft P .fi .UNINDENT .UNINDENT .sp Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP\&. .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-status.10000644000004100000410000001535612520074675021456 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-STATUS" "1" "Chef 12.0" "" "knife status" .SH NAME knife-status \- The man page for the knife status subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife status\fP subcommand is used to display a brief summary of the nodes on a Chef server, including the time of the most recent successful chef\-client run. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife status (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-H\fP, \fB\-\-hide\-healthy\fP Use to hide nodes on which a chef\-client run has occurred within the previous hour. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fBSEARCH_QUERY\fP The search query used to identify a a list of items on a Chef server\&. This option uses the same syntax as the \fBsearch\fP sub\-command. .TP .B \fB\-r RUN_LIST\fP, \fB\-\-run\-list RUN_LIST\fP A comma\-separated list of roles and/or recipes to be applied. .TP .B \fB\-s\fP, \fB\-\-sort\-reverse\fP Use to sort a list by last run time, descending. .TP .B \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To include run lists in the status, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife status \-\-run\-list .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 20 hours ago, dev\-vm.chisamore.com, ubuntu 10.04, dev\-vm.chisamore.com, 10.66.44.126, role[lb]. 3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web]. 3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206, role[web]. 3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250, role[web]. 3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230, role[web]. 3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141, role[web]. .ft P .fi .UNINDENT .UNINDENT .sp To show the status for nodes on which the chef\-client did not run successfully within the past hour, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife status \-\-hide\-healthy .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 1 hour ago, i\-256f884f, ubuntu 12.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web]. 1 hour ago, i\-a47823c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 184.129.143.111, role[lb]. .ft P .fi .UNINDENT .UNINDENT .sp To show the status of a subset of nodes that are returned by a specific query, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife status "role:web" \-\-run\-list .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102, role[web]. 3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206, role[web]. 3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250, role[web]. 3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230, role[web]. 3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141, role[web]. .ft P .fi .UNINDENT .UNINDENT .sp To view the status of all nodes in the organization, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife status .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 20 hours ago, dev\-vm.chisamore.com, ubuntu 10.04, dev\-vm.chisamore.com, 10.66.44.126 3 hours ago, i\-225f954f, ubuntu 10.04, ec2\-67\-202\-63\-102.compute\-1.amazonaws.com, 67.202.63.102 3 hours ago, i\-a45298c9, ubuntu 10.04, ec2\-174\-129\-127\-206.compute\-1.amazonaws.com, 174.129.127.206 3 hours ago, i\-5272a43f, ubuntu 10.04, ec2\-184\-73\-9\-250.compute\-1.amazonaws.com, 184.73.9.250 3 hours ago, i\-226ca64f, ubuntu 10.04, ec2\-75\-101\-240\-230.compute\-1.amazonaws.com, 75.101.240.230 3 hours ago, i\-f65c969b, ubuntu 10.04, ec2\-184\-73\-60\-141.compute\-1.amazonaws.com, 184.73.60.141 .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-user.10000644000004100000410000001772712520074675021115 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-USER" "1" "Chef 12.0" "" "knife user" .SH NAME knife-user \- The man page for the knife user subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife user\fP subcommand is used to manage the list of users and their associated RSA public key\-pairs. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 This subcommand ONLY works when run against the open source Chef server version 10.x. This subcommand will NOT run against open source Chef server 11, Enterprise Chef (including hosted Enterprise Chef), or Private Chef\&. .UNINDENT .UNINDENT .SH COMMON OPTIONS .sp The following options may be used with any of the arguments available to the \fBknife user\fP subcommand: .INDENT 0.0 .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH CREATE .sp The \fBcreate\fP argument is used to create a user. This process will generate an RSA key pair for the named user. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file. .INDENT 0.0 .IP \(bu 2 For the user, the private key should be copied to the system as \fB/etc/chef/client.pem\fP\&. .IP \(bu 2 For knife, the private key is typically copied to \fB~/.chef/client_name.pem\fP and referenced in the knife.rb configuration file. .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user create USER_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a\fP, \fB\-\-admin\fP Use to create a client as an admin client. This is required for any user to access Open Source Chef as an administrator. This option only works when used with the open source Chef server and will have no effect when used with Enterprise Chef\&. .TP .B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP Use to save a private key to the specified file name. .TP .B \fB\-p PASSWORD\fP, \fB\-\-password PASSWORD\fP The user password. .TP .B \fB\-\-user\-key FILE_NAME\fP All users are assigned a public key. Use to write the public key to a file. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user create "Radio Birdman" \-f /keys/user_name .ft P .fi .UNINDENT .UNINDENT .SH DELETE .sp The \fBdelete\fP argument is used to delete a registered user. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user delete USER_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user delete "Steve Danno" .ft P .fi .UNINDENT .UNINDENT .SH EDIT .sp The \fBedit\fP argument is used to edit the details of a user. When this argument is run, knife will open $EDITOR\&. When finished, knife will update the Chef server with those changes. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user edit USER_NAME .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This command does not have any specific options. .sp \fBExamples\fP .sp None. .SH LIST .sp The \fBlist\fP argument is used to view a list of registered users. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user list (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-w\fP, \fB\-\-with\-uri\fP Use to show the corresponding URIs. .UNINDENT .sp \fBExamples\fP .sp None. .SH REREGISTER .sp The \fBreregister\fP argument is used to regenerate an RSA key pair for a user. The public key will be stored on the Chef server and the private key will be displayed on \fBSTDOUT\fP or written to a named file. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Running this argument will invalidate the previous RSA key pair, making it unusable during authentication to the Chef server\&. .UNINDENT .UNINDENT .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user reregister USER_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-f FILE_NAME\fP, \fB\-\-file FILE_NAME\fP Use to save a private key to the specified file name. .UNINDENT .sp \fBExamples\fP .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user reregister "Robert Younger" .ft P .fi .UNINDENT .UNINDENT .SH SHOW .sp The \fBshow\fP argument is used to show the details of a user. .sp \fBSyntax\fP .sp This argument has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user show USER_NAME (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This argument has the following options: .INDENT 0.0 .TP .B \fB\-a ATTR\fP, \fB\-\-attribute ATTR\fP The attribute (or attributes) to show. .UNINDENT .sp \fBExamples\fP .sp To view a user named \fBDennis Teck\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user show "Dennis Teck" .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C chef_type: user json_class: Chef::User name: Dennis Teck public_key: .ft P .fi .UNINDENT .UNINDENT .sp To view information in JSON format, use the \fB\-F\fP common option as part of the command like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife user show "Dennis Teck" \-F json .ft P .fi .UNINDENT .UNINDENT .sp (Other formats available include \fBtext\fP, \fByaml\fP, and \fBpp\fP, e.g. \fB\-F yaml\fP for YAML\&.) .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-upload.10000644000004100000410000001550312520074675021411 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-UPLOAD" "1" "Chef 12.0" "" "knife upload" .SH NAME knife-upload \- The man page for the knife upload subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife upload\fP subcommand is used to upload roles, cookbooks, environments, and data bags to the Chef server from the current working directory in the chef\-repo\&. This subcommand is often used in conjunction with \fBknife diff\fP, which can be used to see exactly what changes will be uploaded, and then \fBknife download\fP, which does the opposite of \fBknife upload\fP\&. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-\-[no\-]diff\fP Use to upload only new and modified files. Set to \fBfalse\fP to upload all files. Default: \fBtrue\fP\&. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-\-[no\-]force\fP Use \fB\-\-force\fP to upload roles, cookbooks, etc. even if the file in the directory is identical (by default, no \fBPOST\fP or \fBPUT\fP is performed unless an actual change would be made). Default: \fB\-\-no\-force\fP\&. .TP .B \fB\-\-[no\-]freeze\fP Use to require changes to a cookbook be included as a new version. Only the \fB\-\-force\fP option can override this setting. Default: \fBfalse\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-n\fP, \fB\-\-dry\-run\fP Use to take no action and only print out results. Default: \fBfalse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-[no\-]purge\fP Use \fB\-\-purge\fP to delete roles, cookbooks, etc. from the Chef server if their corresponding files do not exist in the chef\-repo\&. By default, such objects are left alone and NOT purged. Default: \fB\-\-no\-purge\fP\&. .TP .B \fB\-\-[no\-]recurse\fP Use \fB\-\-no\-recurse\fP to disable uploading a directory recursively. Default: \fB\-\-recurse\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload / .ft P .fi .UNINDENT .UNINDENT .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload cookbooks .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload /cookbooks .ft P .fi .UNINDENT .UNINDENT .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload environments .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload /environments .ft P .fi .UNINDENT .UNINDENT .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload environments/production.json .ft P .fi .UNINDENT .UNINDENT .sp or from the \fBenvironments/\fP directory, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload production.json .ft P .fi .UNINDENT .UNINDENT .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload roles .ft P .fi .UNINDENT .UNINDENT .sp or from anywhere in the chef\-repo, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload /roles .ft P .fi .UNINDENT .UNINDENT .sp Browse to the top level of the chef\-repo and enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload cookbooks/apache\e* roles/webserver.json .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife upload \(gaknife deps nodes/*.json\(ga .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife.10000644000004100000410000002306512520074675020131 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE" "1" "Chef 12.0" "" "knife" .SH NAME knife \- The man page for the knife command line tool. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp knife is a command\-line tool that provides an interface between a local chef\-repo and the Chef server\&. knife helps users to manage: .INDENT 0.0 .IP \(bu 2 Nodes .IP \(bu 2 Cookbooks and recipes .IP \(bu 2 Roles .IP \(bu 2 Stores of JSON data (data bags), including encrypted data .IP \(bu 2 Environments .IP \(bu 2 Cloud resources, including provisioning .IP \(bu 2 The installation of the chef\-client on management workstations .IP \(bu 2 Searching of indexed data on the Chef server .UNINDENT .sp Knife subcommands: .INDENT 0.0 .IP \(bu 2 knife bootstrap .IP \(bu 2 knife client .IP \(bu 2 knife configure .IP \(bu 2 knife cookbook .IP \(bu 2 knife cookbook site .IP \(bu 2 knife data bag .IP \(bu 2 knife delete .IP \(bu 2 knife deps .IP \(bu 2 knife diff .IP \(bu 2 knife download .IP \(bu 2 knife edit .IP \(bu 2 knife environment .IP \(bu 2 knife exec .IP \(bu 2 knife list .IP \(bu 2 knife node .IP \(bu 2 knife raw .IP \(bu 2 knife recipe list .IP \(bu 2 knife role .IP \(bu 2 knife search .IP \(bu 2 knife show .IP \(bu 2 knife ssh .IP \(bu 2 knife status .IP \(bu 2 knife tag .IP \(bu 2 knife upload .IP \(bu 2 knife user .IP \(bu 2 knife xargs .UNINDENT .SH WORKING WITH KNIFE .sp knife runs from a management workstation and sits in\-between a Chef server and an organization\(aqs infrastructure. knife interacts with a Chef server by using the same REST API that is used by a chef\-client\&. Role\-based authentication controls (RBAC) can be used to authorize changes when knife is run with Enterprise Chef\&. knife is configured during workstation setup, but subsequent modifications can be made using the knife.rb configuration file. .SS Common Options .sp The following options can be run with all knife sub\-commands and plug\-ins: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. For example, when knife is run from a node that is configured to be managed by the Chef server, this option is used to allow knife to use the same credentials as the chef\-client when communicating with the Chef server\&. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. This option is ignored during search queries made using the \fBknife search\fP subcommand. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SS JSON Data Format .sp Most data is entered using a text editor in JSON format, unless the \fB\-\-disable\-editing\fP option is entered as part of a command. (Encrypted data bags use YAML, which is a superset of JSON\&.) JSON is a common, language\-independent data format that provides a simple text representation of arbitrary data structures. For more information about JSON, see \fI\%http://www.json.org/\fP or \fI\%http://en.wikipedia.org/wiki/JSON\fP\&. .SS Set the Text Editor .sp Some knife commands, such as \fBknife data bag edit\fP, require that information be edited as JSON data using a text editor. For example, the following command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife data bag edit admins admin_name .ft P .fi .UNINDENT .UNINDENT .sp will open up the text editor with data similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "id": "admin_name" } .ft P .fi .UNINDENT .UNINDENT .sp Changes to that file can then be made: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C { "id": "Justin C." "description": "I am passing the time by letting time pass over me ..." } .ft P .fi .UNINDENT .UNINDENT .sp The type of text editor that is used by knife can be configured by adding an entry to the knife.rb file or by setting an \fBEDITOR\fP environment variable. For example, to configure the text editor to always open with vim, add the following to the knife.rb file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = "/usr/bin/vim" .ft P .fi .UNINDENT .UNINDENT .sp When a Microsoft Windows file path is enclosed in a double\-quoted string (" "), the same backslash character (\fB\e\fP) that is used to define the file path separator is also used in Ruby to define an escape character. The knife.rb file is a Ruby file; therefore, file path separators must be escaped. In addition, spaces in the file path must be replaced with \fB~1\fP so that the length of each section within the file path is not more than 8 characters. For example, if EditPad Pro is the text editor of choice and is located at the following path: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C C:\e\eProgram Files (x86)\eEditPad Pro\eEditPad.exe .ft P .fi .UNINDENT .UNINDENT .sp the setting in the knife.rb file would be similar to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = "C:\e\eProgra~1\e\eEditPa~1\e\eEditPad.exe" .ft P .fi .UNINDENT .UNINDENT .sp One approach to working around the double\- vs. single\-quote issue is to put the single\-quotes outside of the double\-quotes. For example, for Notepad++: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = \(aq"C:\eProgram Files (x86)\eNotepad++\enotepad++.exe \-nosession \-multiInst"\(aq .ft P .fi .UNINDENT .UNINDENT .sp for Sublime Text: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = \(aq"C:\eProgram Files\eSublime Text 2\esublime_text.exe \-\-wait"\(aq .ft P .fi .UNINDENT .UNINDENT .sp for TextPad: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = \(aq"C:\eProgram Files (x86)\eTextPad 7\eTextPad.exe"\(aq .ft P .fi .UNINDENT .UNINDENT .sp and for vim: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C knife[:editor] = \(aq"C:\eProgram Files (x86)\evim\evim74\egvim.exe"\(aq .ft P .fi .UNINDENT .UNINDENT .SS Using Quotes .sp Values can be entered with double quotes (" ") or single quotes (\(aq \(aq), but this should be done consistently. .SS Sub\-commands .sp knife comes with a collection of built in subcommands that work together to provide all of the functionality required to take specific actions against any object in an organization, including cookbooks, nodes, roles, data bags, environments, and users. A knife plugin extends the functionality beyond built\-in subcommands. .sp knife has the following subcommands: \fBbootstrap\fP, \fBclient\fP, \fBconfigure\fP, \fBcookbook\fP, \fBcookbook site\fP, \fBdata bag\fP, \fBdelete\fP, \fBdeps\fP, \fBdiff\fP, \fBdownload\fP, \fBedit\fP, \fBenvironment\fP, \fBexec\fP, \fBindex rebuild\fP, \fBlist\fP, \fBnode\fP, \fBrecipe list\fP, \fBrole\fP, \fBsearch\fP, \fBshow\fP, \fBssh\fP, \fBstatus\fP, \fBtag\fP, \fBupload\fP, \fBuser\fP, and \fBxargs\fP\&. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 The following subcommands run only against the open source Chef server: \fBindex rebuild\fP and \fBuser\fP\&. .UNINDENT .UNINDENT .SS Syntax .sp All knife subcommands have the following syntax: .INDENT 0.0 .INDENT 3.5 knife subcommand [ARGUMENT] (options) .UNINDENT .UNINDENT .sp Each subcommand has its own set of arguments and options. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 All syntax examples in this document show variables in ALL_CAPS. For example \fB\-u PORT_LIST\fP (where PORT_LIST is a comma\-separated list of local and public UDP ports) or \fB\-F FORMAT\fP (where FORMAT determines the output format, either \fBsummary\fP, \fBtext\fP, \fBjson\fP, \fByaml\fP, or \fBpp\fP). These variables often require specific values that are unique to each organization. .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-edit.10000644000004100000410000001004712520074675021050 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-EDIT" "1" "Chef 12.0" "" "knife edit" .SH NAME knife-edit \- The man page for the knife edit subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife edit\fP subcommand is used to edit objects on the Chef server\&. This subcommand works similar to \fBknife cookbook edit\fP, \fBknife data bag edit\fP, \fBknife environment edit\fP, \fBknife node edit\fP, and \fBknife role edit\fP, but with a single verb (and a single action). .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife edit (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-local\fP Use to show files in the local chef\-repo instead of a remote location. Default: \fBfalse\fP\&. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default: \fBeverything\fP / \fBhosted_everything\fP\&. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/README.md0000644000004100000410000000405512520074675020230 0ustar www-datawww-data# Man pages for Knife The source of the Chef Documentation is located at http://docs.opscode.com/. This README documents how the man pages for all of the Knife subcommands that are built into the chef-client are managed. ## Source Files The source files are located in the chef-docs repository: https://github.com/opscode/chef-docs Each Knife subcommand has its own source folder. The folder naming pattern begins with man_. Each man page is a single file called index.html. In the conf.py file, the following settings are unique to each man page: `today` setting is used to define the Chef version. This is because we don't want an arbitrary date populated in the file, yet we still need a version number. For example: `today = 'Chef 11.8`. `project` setting is set to be the same as the name of the subcommand. For example: `project = u'knife-foo'`. `Options for man page output` settings are set to be similar across all man pages, but each one needs to be tailored specifically for the name of the man page. All of the other settings in the General Configuration section should be left alone. These exist to ensure that all of the doc builds are sharing the right common elements and have the same overall presentation. ## Building Docs The docs are built using Sphinx and must be set to the `-b man` output. Currently, the man pages are built locally and then added to the Chef builds in chef-master. ## Editing These files should never be edited. All of the content is pulled in from elsewhere in the chef-docs repo at build time. If changes need to be made, those changes are done elsewhere and then the man pages must be rebuilt. This is to help ensure that all of the changes are made across all of the locations in which these documents need to live. For example, by design, every Knife subcommand with a man page also has an HTML doc at docs.opscode.com/knife_foo.html. ## License [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/) ## Questions? Open an [Issue](https://github.com/opscode/chef-docs/issues) and ask. chef-12.3.0/distro/common/man/man1/knife-ssh.10000644000004100000410000002470012520074675020721 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-SSH" "1" "Chef 12.0" "" "knife ssh" .SH NAME knife-ssh \- The man page for the knife ssh subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife ssh\fP subcommand is used to invoke SSH commands (in parallel) on a subset of nodes within an organization, based on the results of a \fI\%search query\fP made to the Chef server\&. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh SEARCH_QUERY SSH_COMMAND (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-a SSH_ATTR\fP, \fB\-\-attribute SSH_ATTR\fP The attribute that is used when opening the SSH connection. The default attribute is the FQDN of the host. Other possible values include a public IP address, a private IP address, or a hostname. .TP .B \fB\-A\fP, \fB\-\-forward\-agent\fP Use to enable SSH agent forwarding. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-C NUM\fP, \fB\-\-concurrency NUM\fP The number of allowed concurrent connections. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-G GATEWAY\fP, \fB\-\-ssh\-gateway GATEWAY\fP The SSH tunnel or gateway that is used to run a bootstrap action on a machine that is not accessible from the workstation. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-i IDENTITY_FILE\fP, \fB\-\-identity\-file IDENTIFY_FILE\fP The SSH identity file used for authentication. Key\-based authentication is recommended. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-m\fP, \fB\-\-manual\-list\fP Use to define a search query as a space\-separated list of servers. If there is more than one item in the list, put quotes around the entire list. For example: \fB\-\-manual\-list "server01 server 02 server 03"\fP .TP .B \fB\-\-[no\-]host\-key\-verify\fP Use \fB\-\-no\-host\-key\-verify\fP to disable host key verification. Default setting: \fB\-\-host\-key\-verify\fP\&. .TP .B \fBOTHER\fP The shell type. Possible values: \fBinteractive\fP, \fBscreen\fP, \fBtmux\fP, \fBmacterm\fP, or \fBcssh\fP\&. (\fBcsshx\fP is deprecated in favor of \fBcssh\fP\&.) .TP .B \fB\-p PORT\fP, \fB\-\-ssh\-port PORT\fP The SSH port. .TP .B \fB\-P PASSWORD\fP, \fB\-\-ssh\-password PASSWORD\fP The SSH password. This can be used to pass the password directly on the command line. If this option is not specified (and a password is required) knife will prompt for the password. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-s URL\fP, \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fBSEARCH_QUERY\fP The search query used to return a list of servers to be accessed using SSH and the specified \fBSSH_COMMAND\fP\&. This option uses the same syntax as the search sub\-command. .TP .B \fBSSH_COMMAND\fP The command that will be run against the results of a search query. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-x USER_NAME\fP, \fB\-\-ssh\-user USER_NAME\fP The SSH user name. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp To find the uptime of all of web servers running Ubuntu on the Amazon EC2 platform, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh "role:web" "uptime" \-x ubuntu \-a ec2.public_hostname .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ec2\-174\-129\-127\-206.compute\-1.amazonaws.com 13:50:47 up 1 day, 23:26, 1 user, load average: 0.25, 0.18, 0.11 ec2\-67\-202\-63\-102.compute\-1.amazonaws.com 13:50:47 up 1 day, 23:33, 1 user, load average: 0.12, 0.13, 0.10 ec2\-184\-73\-9\-250.compute\-1.amazonaws.com 13:50:48 up 16:45, 1 user, load average: 0.30, 0.22, 0.13 ec2\-75\-101\-240\-230.compute\-1.amazonaws.com 13:50:48 up 1 day, 22:59, 1 user, load average: 0.24, 0.17, 0.11 ec2\-184\-73\-60\-141.compute\-1.amazonaws.com 13:50:48 up 1 day, 23:30, 1 user, load average: 0.32, 0.17, 0.15 .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh \(aqname:*\(aq \(aqsudo chef\-client\(aq .ft P .fi .UNINDENT .UNINDENT .sp To force a chef\-client run on all of the web servers running Ubuntu on the Amazon EC2 platform, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh "role:web" "sudo chef\-client" \-x ubuntu \-a ec2.public_hostname .ft P .fi .UNINDENT .UNINDENT .sp to return something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:37 +0000] INFO: Starting Chef Run (Version 0.9.10) ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:37 +0000] INFO: Starting Chef Run (Version 0.9.10) ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10) ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10) ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:38 +0000] INFO: Starting Chef Run (Version 0.9.10) ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Chef Run complete in 1.419243 seconds ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: cleaning the checksum cache ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Running report handlers ec2\-174\-129\-127\-206.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Report handlers complete ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Chef Run complete in 1.578265 seconds ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: cleaning the checksum cache ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Running report handlers ec2\-67\-202\-63\-102.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:39 +0000] INFO: Report handlers complete ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.638884 seconds ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers ec2\-184\-73\-9\-250.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.540257 seconds ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers ec2\-75\-101\-240\-230.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Chef Run complete in 1.502489 seconds ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: cleaning the checksum cache ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Running report handlers ec2\-184\-73\-60\-141.compute\-1.amazonaws.com [Fri, 22 Oct 2010 14:18:40 +0000] INFO: Report handlers complete .ft P .fi .UNINDENT .UNINDENT .sp To query for all nodes that have the \fBwebserver\fP role and then use SSH to run the command \fBsudo chef\-client\fP, enter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh "role:webserver" "sudo chef\-client" .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh name:* "sudo aptitude upgrade \-y" .ft P .fi .UNINDENT .UNINDENT .sp To specify the shell type used on the nodes returned by a search query: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife ssh roles:opscode\-omnitruck macterm .ft P .fi .UNINDENT .UNINDENT .sp where \fBscreen\fP is one of the following values: \fBcssh\fP, \fBinteractive\fP, \fBmacterm\fP, \fBscreen\fP, or \fBtmux\fP\&. If the node does not have the shell type installed, knife will return an error similar to the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C you need the rb\-appscript gem to use knife ssh macterm. \(ga(sudo) gem install rb\-appscript\(ga to install ERROR: LoadError: cannot load such file \-\- appscript .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/man/man1/knife-xargs.10000644000004100000410000001376012520074675021254 0ustar www-datawww-data.\" Man page generated from reStructuredText. . .TH "KNIFE-XARGS" "1" "Chef 12.0" "" "knife xargs" .SH NAME knife-xargs \- The man page for the knife xargs subcommand. . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp The \fBknife xargs\fP subcommand is used to take patterns from standard input, download as JSON, run a command against the downloaded JSON, and then upload any changes. .sp \fBSyntax\fP .sp This subcommand has the following syntax: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife xargs [PATTERN...] (options) .ft P .fi .UNINDENT .UNINDENT .sp \fBOptions\fP .sp This subcommand has the following options: .INDENT 0.0 .TP .B \fB\-0\fP Use to show a \fBNULL\fP character (\fB\e0\fP) instead of white space as the separator. Default: \fBfalse\fP\&. .TP .B \fB\-\-chef\-repo\-path PATH\fP The path to the chef\-repo\&. This setting will override the default path to the chef\-repo\&. Default: same as specified by \fBchef_repo_path\fP in config.rb. .TP .B \fB\-\-concurrency\fP The number of allowed concurrent connections. Default: \fB10\fP\&. .TP .B \fB\-\-[no\-]diff\fP Use to show a diff when a file changes. Default: \fB\-\-diff\fP\&. .TP .B \fB\-\-dry\-run\fP Use to prevent changes from being uploaded to the Chef server\&. Default: \fBfalse\fP\&. .TP .B \fB\-\-[no\-]force\fP Use to force the upload of files even if they haven\(aqt been changed. Default: \fB\-\-no\-force\fP\&. .TP .B \fB\-I REPLACE_STRING\fP, \fB\-\-replace REPLACE_STRING\fP Use to define a string that will be used to replace all occurrences of a file name. Default: \fBnil\fP\&. .TP .B \fB\-J REPLACE_STRING\fP, \fB\-\-replace\-first REPLACE_STRING\fP Use to define a string that will be used to replace the first occurrence of a file name. Default: \fBnil\fP\&. .TP .B \fB\-\-local\fP Use to build or execute a command line against a local file. Set to \fBfalse\fP to build or execute against a remote file. Default: \fBfalse\fP\&. .TP .B \fB\-n MAX_ARGS\fP, \fB\-\-max\-args MAX_ARGS\fP The maximum number of arguments per command line. Default: \fBnil\fP\&. .TP .B \fB\-p [PATTERN...]\fP, \fB\-\-pattern [PATTERN...]\fP One (or more) patterns for a command line. If this option is not specified, a list of patterns may be expected on standard input. Default: \fBnil\fP\&. .TP .B \fB\-\-repo\-mode MODE\fP The layout of the local chef\-repo\&. Possible values: \fBstatic\fP, \fBeverything\fP, or \fBhosted_everything\fP\&. Use \fBstatic\fP for just roles, environments, cookbooks, and data bags. By default, \fBeverything\fP and \fBhosted_everything\fP are dynamically selected depending on the server type. Default value: \fBdefault\fP\&. .TP .B \fB\-s LENGTH\fP, \fB\-\-max\-chars LENGTH\fP The maximum size (in characters) for a command line. Default: \fBnil\fP\&. .TP .B \fB\-t\fP Use to run the print command on the command line. Default: \fBnil\fP\&. .TP .B \fB\-c CONFIG_FILE\fP, \fB\-\-config CONFIG_FILE\fP The configuration file to use. .TP .B \fB\-\-chef\-zero\-port PORT\fP The port on which chef\-zero will listen. .TP .B \fB\-d\fP, \fB\-\-disable\-editing\fP Use to prevent the $EDITOR from being opened and to accept data as\-is. .TP .B \fB\-\-defaults\fP Use to have knife use the default value instead of asking a user to provide one. .TP .B \fB\-e EDITOR\fP, \fB\-\-editor EDITOR\fP The $EDITOR that is used for all interactive commands. .TP .B \fB\-E ENVIRONMENT\fP, \fB\-\-environment ENVIRONMENT\fP The name of the environment. When this option is added to a command, the command will run only against the named environment. .TP .B \fB\-F FORMAT\fP, \fB\-\-format FORMAT\fP The output format: \fBsummary\fP (default), \fBtext\fP, \fBjson\fP, \fByaml\fP, and \fBpp\fP\&. .TP .B \fB\-h\fP, \fB\-\-help\fP Shows help for the command. .TP .B \fB\-k KEY\fP, \fB\-\-key KEY\fP The private key that knife will use to sign requests made by the API client to the Chef server\&. .TP .B \fB\-\-[no\-]color\fP Use to view colored output. .TP .B \fB\-\-print\-after\fP Use to show data after a destructive operation. .TP .B \fB\-\-server\-url URL\fP The URL for the Chef server\&. .TP .B \fB\-u USER\fP, \fB\-\-user USER\fP The user name used by knife to sign requests made by the API client to the Chef server\&. Authentication will fail if the user name does not match the private key. .TP .B \fB\-v\fP, \fB\-\-version\fP The version of the chef\-client\&. .TP .B \fB\-V\fP, \fB\-\-verbose\fP Set for more verbose outputs. Use \fB\-VV\fP for maximum verbosity. .TP .B \fB\-y\fP, \fB\-\-yes\fP Use to respond to all confirmation prompts with "Yes". knife will not ask for confirmation. .TP .B \fB\-z\fP, \fB\-\-local\-mode\fP Use to run the chef\-client in local mode. This allows all commands that work against the Chef server to also work against the local chef\-repo\&. .UNINDENT .sp \fBExamples\fP .sp The following examples show various ways of listing all nodes on the server, and then using Perl to replace \fBgrantmc\fP with \fBgmc\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list \(aqnodes/*\(aq | knife xargs "perl \-i \-pe \(aqs/grantmc/gmc\(aq" .ft P .fi .UNINDENT .UNINDENT .sp or without quotes and the backslash escaped: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife list /nodes/\e* | knife xargs "perl \-i \-pe \(aqs/grantmc/gmc\(aq" .ft P .fi .UNINDENT .UNINDENT .sp or by using the \fB\-\-pattern\fP option: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ knife xargs \-\-pattern \(aq/nodes.*\(aq "perl \-i \-pe \(aqs/grantmc/gmc\(aq" .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR Chef .\" Generated by docutils manpage writer. . chef-12.3.0/distro/common/markdown/0000755000004100000410000000000012520074675017160 5ustar www-datawww-datachef-12.3.0/distro/common/markdown/man8/0000755000004100000410000000000012520074675020023 5ustar www-datawww-datachef-12.3.0/distro/common/markdown/man8/chef-expanderctl.mkd0000644000004100000410000000344312520074675023740 0ustar www-datawww-datachef-expanderctl(8) -- management program for chef-expander ======================================== ## SYNOPSIS __chef-expanderctl__ _COMMAND_ __Commands:__ * `help`: Show help message * `queue-depth`: display the aggregate queue backlog * `queue-status`: show the backlog and consumer count for each vnode queue * `node-status`: show the status of the nodes in the cluster * `log-level`: sets the log level of all nodes in the cluster ## DESCRIPTION Chef-expanderctl is a management program that allows you to get status information or change the logging verbosity (without restarting). chef-expanderctl has the following commands: * __chef-expanderctl help__ prints usage. * __chef-expanderctl queue-depth__ Shows the total number of messages in the queues. * __chef-expanderctl queue-status__ Show the number of messages in each queue. This is mainly of use when debugging a Chef Expander cluster. * __chef-expanderctl log-level LEVEL__ Sets the log level on a running Chef Expander or cluster. If you suspect that a worker process is stuck, as long as you are using clustered operation, you can simply kill the worker process and it will be restarted by the master process. ## SEE ALSO __chef-expander-cluster__(8) __chef-solr__(8) Full documentation for Chef and chef-server is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was created by Nuo Yan . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-server-webui.mkd0000644000004100000410000001210412520074675024040 0ustar www-datawww-datachef-server-webui(8) -- Start the Chef Server merb application slice providing Web User Interface (Management Console). ======================================== ## SYNOPSIS __chef-server-webui__ _(options)_ * `-u`, `--user USER`: This flag is for having chef-server-webui run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect. * `-G`, `--group GROUP`: This flag is for having chef-server-webui run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect. * `-d`, `--daemonize`: This will run a single chef-server-webui in the background. * `-N`, `--no-daemonize`: This will allow you to run a cluster in console mode. * `-c`, `--cluster-nodes NUM_MERBS`: Number of merb daemons to run for chef-server-webui. * `-I`, `--init-file FILE`: File to use for initialization on load, defaults to config/init.rb. * `-p`, `--port PORTNUM`: Port to run chef-server-webui on, defaults to 4040. Additional nodes (-c) listen on incrementing port numbers. * `-o`, `--socket-file FILE`: Socket file to run chef-server-webui on, defaults to [Merb.root]/log/merb.sock. This is for web servers, like thin, that use sockets. Specify this *only* if you *must*. * `-s`, `--socket SOCKNUM`: Socket number to run chef-server-webui on, defaults to 0. * `-n`, `--name NAME`: Set the name of the application. This is used in the process title and log file names. * `-P`, `--pid PIDFILE`: PID file, defaults to [Merb.root]/log/merb.main.pid for the master process and[Merb.root]/log/merb.[port number].pid for worker processes. For clusters, use %s to specify where in the file chef-server-webui should place the port number. For instance: -P myapp.%s.pid. * `-h`, `--host HOSTNAME`: Host to bind to (default is 0.0.0.0). * `-m`, `--merb-root PATH_TO_APP_ROOT`: The path to the Merb.root for the app you want to run (default is current working directory). * `-a`, `--adapter ADAPTER`: The rack adapter to use to run chef-server-webui (default is mongrel) [mongrel, emongrel, thin, ebb, fastcgi, webrick]. * `-R`, `--rackup FILE`: Load an alternate Rack config file (default is config/rack.rb). * `-i`, `--irb-console`: This flag will start chef-server-webui in irb console mode. All your models and other classes will be available for you in an irb session. * `-S`, `--sandbox`: This flag will enable a sandboxed irb console. If your ORM supports transactions, all edits will be rolled back on exit. * `-l`, `--log-level LEVEL`: Log levels can be set to any of these options: debug < info < warn < error < fatal (default is info). * `-L`, `--log LOGFILE`: A string representing the logfile to use. Defaults to [Merb.root]/log/merb.[main].log for the master process and [Merb.root]/log/merb[port number].logfor worker processes. * `-e`, `--environment STRING`: Environment to run Merb under [development, production, testing] (default is development). * `-r`, `--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]`: Command-line option to run scripts and/or code in the chef-server-webui app. * `-K`, `-graceful PORT or all`: Gracefully kill chef-server-webui proceses by port number. Use chef-server -K all to gracefully kill all merbs. * `-k`, `--kill PORT`: Force kill one merb worker by port number. This will cause the worker to be respawned. * `--fast-deploy`: Reload the code, but not yourinit.rb or gems. * `-X`, `--mutex on/off`: This flag is for turning the mutex lock on and off. * `-D`, `--debugger`: Run chef-server-webui using rDebug. * `-V`, `--verbose`: Print extra information. * `-C`, `--console-trap`: Enter an irb console on ^C. * `-?`, `-H`, `--help`: Show this help message. ## DESCRIPTION The Chef Server WebUI (Management Console) is a Merb application slice. The default listen port is 4040. The Management Console is Chef Server's web interface. Nodes, roles, cookbooks, data bags, and API clients can be managed through the Management Console. Search can also be done on the console. In order to start using the Management Console, you need to first create a user or change the default password on the "admin" user. The default credentials are: - `Username`: admin - `Password`: p@ssw0rd1 ## SEE ALSO Full documentation for Chef and chef-server-webui (Management Console) is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was written by Joshua Timberman 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. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-solo.mkd0000644000004100000410000000744012520074675022404 0ustar www-datawww-datachef-solo(8) -- Runs chef in solo mode against a specified cookbook location. ======================================== ## SYNOPSIS __chef-solo__ _(options)_ * `-c`, `--config CONFIG`: The configuration file to use * `-d`, `--daemonize`: Daemonize the process * `-g`, `--group GROUP`: Group to set privilege to * `-i`, `--interval SECONDS`: Run chef-client periodically, in seconds * `-j`, `--json-attributes JSON_ATTRIBS`: Load attributes from a JSON file or URL * `-l`, `--log_level LEVEL`: Set the log level (debug, info, warn, error, fatal) * `-L`, `--logfile LOGLOCATION`: Set the log file location, defaults to STDOUT - recommended for daemonizing * `-N`, `--node-name NODE_NAME`: The node name for this client * `-r`, `--recipe-url RECIPE_URL`: Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache. * `-s`, `--splay SECONDS`: The splay time for running at intervals, in seconds * `-u`, `--user USER`: User to set privilege to * `-v`, `--version`: Show chef version * `-h`, `--help`: Show this message ## DESCRIPTION Chef Solo allows you to run Chef Cookbooks in the absence of a Chef Server. To do this, the complete cookbook needs to be present on disk. By default Chef Solo will look in /etc/chef/solo.rb for its configuration. This configuration file has two required variables: file_cache_path and cookbook_path. For example: file_cache_path "/var/chef-solo" cookbook_path "/var/chef-solo/cookbooks" For your own systems, you can change this to reflect any directory you like, but you'll need to specify absolute paths and the cookbook_path directory should be a subdirectory of the file_cache_path. You can also specify cookbook_path as an array, passing multiple locations to search for cookbooks. For example: file_cache_path "/var/chef-solo" cookbook_path ["/var/chef-solo/cookbooks", "/var/chef-solo/site-cookbooks"] Note that earlier entries are now overridden by later ones. Since chef-solo doesn't have any interaction with a Chef Server, you'll need to specify node-specifc attributes in a JSON file. This can be located on the target system itself, or it can be stored on a remote server such as S3, or a web server on your network. Within the JSON file, you'll also specify the recipes that Chef should run in the "run_list". An example JSON file, which sets a resolv.conf: { "resolver": { "nameservers": [ "10.0.0.1" ], "search":"int.example.com" }, "run_list": [ "recipe[resolver]" ] } Then you can run chef-solo with -j to specify the JSON file. It will look for cookbooks in the cookbook_path configured in the configuration file, and apply attributes and use the run_list from the JSON file specified. You can use -c to specify the path to the configuration file (if you don't want chef-solo to use the default). You can also specify -r for a cookbook tarball. For example: chef-solo -c ~/solo.rb -j ~/node.json -r http://www.example.com/chef-solo.tar.gz In the above case, chef-solo would extract the tarball to your specified cookbook_path, use ~/solo.rb as the configuration file, and apply attributes and use the run_list from ~/node.json. ## SEE ALSO Full documentation for Chef and chef-solo is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was written by Joshua Timberman with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-expander.mkd0000644000004100000410000000534612520074675023241 0ustar www-datawww-datachef-expander(8) -- fetches messages from RabbitMQ, processes, and loads into chef-solr ======================================== ## SYNOPSIS __chef-expander__ _(options)_ * `-c`, `--config CONFIG_FILE`: a configuration file to use * `-i`, `--index INDEX`: the slot this node will occupy in the ring * `-n`, `--node-count NUMBER`: the number of nodes in the ring * `-l`, `--log-level LOG_LEVEL`: set the log level * `-L`, `--logfile LOG_LOCATION`: Logfile to use * `-d`, `--daemonize`: fork into the background * `-P`, `--pid PIDFILE`: PID file * `-h`, `--help`: show help message * `-v`, `--version`: show the version and exit ## DESCRIPTION Chef Expander fetches messages from RabbitMQ, processes them into the correct format to be loaded into Solr and loads them into Solr. __Running Chef Expander__ Chef Expander is designed for clustered operation, though small installations will only need one worker process. To run Chef Expander with one worker process, run chef-expander -n 1. You will then have a master and worker process, which looks like this in ps: your-shell> ps aux|grep expander you 52110 0.1 0.7 2515476 62748 s003 S+ 3:49PM 0:00.80 chef-expander worker #1 (vnodes 0-1023) you 52108 0.1 0.5 2492880 41696 s003 S+ 3:49PM 0:00.91 ruby bin/chef-expander -n 1 Workers are single threaded and therefore cannot use more than 100% of a single CPU. If you find that your queues are getting backlogged, increase the number of workers __Design__ Chef Expander uses 1024 queues (called vnodes in some places) to allow you to scale the number of Chef Expander workers to meet the needs of your infrastructure. When objects are saved in the API server, they are added to queues based on their database IDs. These queues can be assigned to different Chef Expander workers to distribute the load of processing the index updates. __Chef Expander Operation and Troubleshooting__ Chef Expander includes chef-expanderctl, a management program that allows you to get status information or change the logging verbosity (without restarting). See __chef-expanderctl__(8) for details. ## SEE ALSO __chef-expanderctl__(8) __chef-solr__(8) Full documentation for Chef and chef-server is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was created by Nuo Yan . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-solr.mkd0000644000004100000410000000577612520074675022421 0ustar www-datawww-datachef-solr(8) -- Runs as Chef's search server ======================================== ## SYNOPSIS __chef-solr__ _(options)_ * `-c`, `--config CONFIG`: The configuration file to use * `-d`, `--daemonize`: Daemonize the process * `-g`, `--group GROUP`: Group to set privilege to * `-l`, `--log_level LEVEL`: Set the log level (debug, info, warn, error, fatal) * `-L`, `--logfile LOGLOCATION`: Set the log file location, defaults to STDOUT - recommended for daemonizing * `-P`, `--pid PIDFILE`: Set the PID file location, defaults to /tmp/chef-solr.pid * `-D`, `--solr-data-dir PATH`: Where the Solr data lives * `-x`, `--solor-heap-size SIZE`: Set the size of the Java Heap * `-H`, `--solr-home-dir PATH`: Solr home directory * `-j`, `--java-opts OPTS`: Raw options passed to Java * `-x`, `--solor-heap-size`: Set the size of the Java Heap * `-W`, `--solr-jetty-dir PATH`: Where to place the Solr Jetty instance * `-u`, `--user USER`: User to set privilege to * `-v`, `--version`: Show chef-solr version * `-h`, `--help`: Show this message ## DESCRIPTION Chef-solr provides search service for Chef. You need to have both chef-solr and chef-expander-cluster running in order for search to work. __Installation__ Make sure you backed up your data if you are upgrading from a previous version. Run chef-solr-installer to upgrade your Chef Solr installation. Answer "yes" when prompted for confirmation. The process should look like this: yourshell> chef-solr-installer Configuration setting solr_heap_size is unknown and will be ignored Chef Solr is already installed in /var/chef/solr Do you want to overwrite the current install? All existing Solr data will be lost. [y/n] y Removing the existing Chef Solr installation rm -rf /var/chef/solr rm -rf /var/chef/solr-jetty rm -rf /var/chef/solr/data Creating Solr Home Directory mkdir -p /var/chef/solr entering /var/chef/solr tar zxvf /Users/ddeleo/opscode/chef/chef-solr/solr/solr-home.tar.gz Creating Solr Data Directory mkdir -p /var/chef/solr/data Unpacking Solr Jetty mkdir -p /var/chef/solr-jetty entering /var/chef/solr-jetty tar zxvf /Users/ddeleo/opscode/chef/chef-solr/solr/solr-jetty.tar.gz Successfully installed Chef Solr. You can restore your search index using `knife index rebuild` ## SEE ALSO __chef-expander-cluster__(8) Full documentation for Chef and chef-server is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was written by Joshua Timberman with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-server.mkd0000644000004100000410000001207512520074675022736 0ustar www-datawww-datachef-server(8) - Start the Chef Server merb application slice. ======================================== ## SYNOPSIS __chef-server__ _(options)_ * `-u`, `--user USER`: This flag is for having chef-server-webui run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect. * `-G`, `--group GROUP`: This flag is for having chef-server-webui run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect. * `-d`, `--daemonize`: This will run a single chef-server-webui in the background. * `-N`, `--no-daemonize`: This will allow you to run a cluster in console mode. * `-c`, `--cluster-nodes NUM_MERBS`: Number of merb daemons to run for chef-server-webui. * `-I`, `--init-file FILE`: File to use for initialization on load, defaults to config/init.rb. * `-p`, `--port PORTNUM`: Port to run chef-server-webui on, defaults to 4040. Additional nodes (-c) listen on incrementing port numbers. * `-o`, `--socket-file FILE`: Socket file to run chef-server-webui on, defaults to [Merb.root]/log/merb.sock. This is for web servers, like thin, that use sockets. Specify this *only* if you *must*. * `-s`, `--socket SOCKNUM`: Socket number to run chef-server-webui on, defaults to 0. * `-n`, `--name NAME`: Set the name of the application. This is used in the process title and log file names. * `-P`, `--pid PIDFILE`: PID file, defaults to [Merb.root]/log/merb.main.pid for the master process and[Merb.root]/log/merb.[port number].pid for worker processes. For clusters, use %s to specify where in the file chef-server-webui should place the port number. For instance: -P myapp.%s.pid. * `-h`, `--host HOSTNAME`: Host to bind to (default is 0.0.0.0). * `-m`, `--merb-root PATH_TO_APP_ROOT`: The path to the Merb.root for the app you want to run (default is current working directory). * `-a`, `--adapter ADAPTER`: The rack adapter to use to run chef-server-webui (default is mongrel) [mongrel, emongrel, thin, ebb, fastcgi, webrick]. * `-R`, `--rackup FILE`: Load an alternate Rack config file (default is config/rack.rb). * `-i`, `--irb-console`: This flag will start chef-server-webui in irb console mode. All your models and other classes will be available for you in an irb session. * `-S`, `--sandbox`: This flag will enable a sandboxed irb console. If your ORM supports transactions, all edits will be rolled back on exit. * `-l`, `--log-level LEVEL`: Log levels can be set to any of these options: debug < info < warn < error < fatal (default is info). * `-L`, `--log LOGFILE`: A string representing the logfile to use. Defaults to [Merb.root]/log/merb.[main].log for the master process and [Merb.root]/log/merb[port number].logfor worker processes. * `-e`, `--environment STRING`: Environment to run Merb under [development, production, testing] (default is development). * `-r`, `--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]`: Command-line option to run scripts and/or code in the chef-server-webui app. * `-K`, `-graceful PORT or all`: Gracefully kill chef-server-webui proceses by port number. Use chef-server -K all to gracefully kill all merbs. * `-k`, `--kill PORT`: Force kill one merb worker by port number. This will cause the worker to be respawned. * `--fast-deploy`: Reload the code, but not yourinit.rb or gems. * `-X`, `--mutex on/off`: This flag is for turning the mutex lock on and off. * `-D`, `--debugger`: Run chef-server-webui using rDebug. * `-V`, `--verbose`: Print extra information. * `-C`, `--console-trap`: Enter an irb console on ^C. * `-?`, `-H`, `--help`: Show this help message. ## DESCRIPTION The Chef Server provides a central point for the distribution of Cookbooks, management and authentication of Nodes, and the use of Search. It provides a REST API. The API service is what clients use to interact with the server to manage node configuration in Chef. By default, the service is started on port 4000 as a Merb application slice running with the thin server adapter. The two methods of interaction with the API for humans are the command-line tool Knife and the Management Console. The Chef Client library is used for interacting with the API for client nodes. ## SEE ALSO __chef-client__(8) __chef-server-webui__(8) __knife__(1) Full documentation for Chef and chef-server is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was written by Joshua Timberman with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/man8/chef-client.mkd0000644000004100000410000000511712520074675022705 0ustar www-datawww-datachef-client(8) -- Runs a client node connecting to a chef-server. ======================================== ## SYNOPSIS __chef-client__ _(options)_ * `-S`, `--server CHEFSERVERURL`: The chef server URL * `-c`, `--config CONFIG`: The configuration file to use * `-d`, `--daemonize`: Daemonize the process * `-g`, `--group GROUP`: Group to set privilege to * `-i`, `--interval SECONDS`: Run chef-client periodically, in seconds * `-j`, `--json-attributes JSON_ATTRIBS`: Load attributes from a JSON file or URL * `-E`, `--environment ENVIRONMENT`: Set the Chef Environment on the node * `-l`, `--log_level LEVEL`: Set the log level (debug, info, warn, error, fatal) * `-L`, `--logfile LOGLOCATION`: Set the log file location, defaults to STDOUT - recommended for daemonizing * `-N`, `--node-name NODE_NAME`: The node name for this client * `-o`, `--override-runlist`: Replace current run list with specified items * `-K`, `--validation_key KEY_FILE`: Set the validation key file location, used for registering new clients * `-k`, `--client_key KEY_FILE`: Set the client key file location * `-s`, `--splay SECONDS`: The splay time for running at intervals, in seconds * `-u`, `--user USER`: User to set privilege to * `-P`, `--pid PIDFILE`: Set the PID file location, defaults to /tmp/chef-client.pid * `--once`: Cancel any interval or splay options, run chef once and exit * `-v`, `--version`: Show chef version * `-h`, `--help`: Show this message ## DESCRIPTION The Chef Client is where almost all of the work in Chef is done. It communicates with the Chef Server via REST, authenticates via Signed Header Authentication, and compiles and executes Cookbooks. A Chef Client does work on behalf of a Node. A single Chef Client can run recipes for multiple Nodes. Clients are where all the action happens - the Chef Server and Chef Expander are largely services that exist only to provide the Client with information. ## SEE ALSO Full documentation for Chef and chef-client is located on the Chef wiki, http://wiki.opscode.com/display/chef/Home. ## AUTHOR Chef was written by Adam Jacob of Opscode (http://www.opscode.com), with contributions from the community. This manual page was written by Joshua Timberman with help2man. Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. On Debian systems, the complete text of the Apache 2.0 License can be found in /usr/share/common-licenses/Apache-2.0. chef-12.3.0/distro/common/markdown/README0000644000004100000410000000023712520074675020042 0ustar www-datawww-dataThis directory contains markdown documentation that is used in other places. For example, markdown (.mkd) documents that are generated as man pages with ronn. chef-12.3.0/distro/common/markdown/man1/0000755000004100000410000000000012520074675020014 5ustar www-datawww-datachef-12.3.0/distro/common/markdown/man1/knife-status.mkd0000644000004100000410000000227712520074675023136 0ustar www-datawww-dataknife-status(1) -- Display status information for the nodes in your infrastructure ======================================== ## SYNOPSIS __knife__ __status__ _(options)_ * `-r`, `--run-list RUN_LIST`: Show the run list ## DESCRIPTION The _status_ sub-command searches the Chef Server for all nodes and displays information about the last time the node checked into the server and executed a `node.save`. The fields displayed are the relative checkin time, the node name, it's operating system platform and version, the fully-qualified domain name and the default IP address. If the `-r` option is given, the node's run list will also be displayed. Note that depending on the configuration of the nodes, the FQDN and IP displayed may not be publicly reachable. ## SEE ALSO __knife-search__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-node.mkd0000644000004100000410000001072412520074675022534 0ustar www-datawww-dataknife-node(1) -- Manage the hosts in your infrastructure ======================================== ## SYNOPSIS __knife__ __node__ _sub-command_ _(options)_ ## DESCRIPTION Nodes are data structures that represent hosts configured with Chef. Nodes have a __name__, a String that uniquely identifies the node, __attributes__, a nested Hash of properties that describe how the host should be configured, a __chef\_environment__, a String representing the environment to which the node belongs, and a __run\_list__, an ordered list of __recipes__ or __roles__ that chef-client should apply when configuring a host. When a host communicates with a Chef Server, it authenticates using its __node\_name__ for identification and signs its reqests with a private key. The Server validates the request by looking up a __client__ object with a name identical to the __node\_name__ submitted with the request and verifes the signature using the public key for that __client__ object. __NOTE__ that the __client__ is a different object in the system. It is associated with a node by virtue of having a matching name. By default __chef-client__(8) will create a node using the FQDN of the host for the node name, though this may be overridden by configuration settings. ## NODE SUB-COMMANDS The following `node` subcommands are available: ## BULK DELETE __knife node bulk delete__ _regex_ _(options)_ Deletes nodes for which the name matches the regular expression _regex_ on the Chef Server. The regular expression should be given in quotes, and should not be surrounded with forward slashes (as is typical of regular expression literals in scripting languages). ## CREATE __knife node create__ _name_ _(options)_ Create a new node. Unless the --disable-editing option is given, an empty node object will be created and displayed in your text editor. If the editor exits with a successful exit status, the node data will be posted to the Chef Server to create the node. ## DELETE __knife node delete__ _name_ _(options)_ Deletes the node identified by _name_ on the Chef Server. ## EDIT __knife node edit__ _name_ _(options)_ * `-a`, `--all`: Display all node data in the editor. By default, default, override, and automatic attributes are not shown. Edit the node identified by _name_. Like __knife node create__, the node will be displayed in your text editor unless the -n option is present. ## FROM FILE __knife node from file__ _file_ _(options)_ Create a node from a JSON format _file_. ## LIST __knife node list__ _(options)_ * `-w`, `--with-uri`: Show corresponding URIs List all nodes. ## RUN\_LIST ADD __knife node run_list add__ _name_ _run list item_ _(options)_ * `-a`, `--after ITEM`: Place the ENTRY in the run list after ITEM Add the _run list item_ to the node's `run_list`. See Run list ## RUN\_LIST REMOVE __knife node run_list remove__ _node name_ _run list item_ _(options)_ Remove the _run list item_ from the node's `run_list`. ## SHOW __knife node show__ _node name_ _(options)_ * `-a`, `--attribute [ATTR]`: Show only one attribute * `-r`, `--run-list `: Show only the run list * `-F`, `--format FORMAT`: Display the node in a different format. * `-m`, `--medium`: Display more, but not all, of the node's data when using the default _summary_ format Displays the node identified by _node name_ on stdout. ## RUN LIST ITEM FORMAT Run list items may be either roles or recipes. When adding a role to a run list, the correct syntax is "role[ROLE\_NAME]" When adding a recipe to a run list, there are several valid formats: * Fully Qualified Format: "recipe[COOKBOOK::RECIPE\_NAME]", for example, "recipe[chef::client]" * Cookbook Recipe Format: For brevity, the recipe part of the fully qualified format may be omitted, and recipes specified as "COOKBOOK::RECIPE\_NAME", e.g., "chef::client" * Default Recipe Format: When adding the default recipe of a cookbook to a run list, the recipe name may be omitted as well, e.g., "chef::default" may be written as just "chef" ## SEE ALSO __knife-client__(1) __knife-search__(1) __knife-role__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-exec.mkd0000644000004100000410000000247312520074675022535 0ustar www-datawww-dataknife-exec(1) -- Run user scripts using the Chef API DSL ======================================== ## SYNOPSIS __knife__ __exec__ _(options)_ * `-E`, `--exec CODE`: Provide a snippet of code to evaluate on the command line ## DESCRIPTION `knife exec` runs arbitrary ruby scripts in a context similar to that of the chef-shell(1) DSL. See the chef-shell documentation for a description of the commands available. ## EXAMPLES * Make an API call against an arbitrary endpoint: knife exec -E 'api.get("nodes/fluke.localdomain/cookbooks")' => list of cookbooks for the node _fluke.localdomain_ * Remove the role _obsolete_ from all nodes: knife exec -E 'nodes.transform(:all){|n| n.run\_list.delete("role[obsolete]")}' * Generate the expanded run list for hosts in the `webserver` role: knife exec -E 'nodes.find(:roles => "webserver") {|n| n.expand!; n[:recipes]}' ## SEE ALSO __chef-shell(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-role.mkd0000644000004100000410000000445212520074675022551 0ustar www-datawww-dataknife-role(1) -- Group common configuration settings ======================================== ## SYNOPSIS __knife__ __role__ _sub-command_ _(options)_ ## ROLE SUB-COMMANDS The following `role` subcommands are available: ## LIST __knife role list__ _(options)_ * `-w`, `--with-uri`: Show corresponding URIs List roles. ## SHOW __knife role show ROLE__ _(options)_ * `-a`, `--attribute ATTR`: Show only one attribute Show a specific role. ## CREATE __knife role create ROLE__ _(options)_ * `-d`, `--description`: The role description Create a new role. ## EDIT __knife role edit ROLE__ _(options)_ Edit a role. ## FROM FILE __knife role from file FILE__ _(options)_ Create or update a role from a role Ruby DSL (`.rb`) or JSON file. ## DELETE __knife role delete ROLE__ _(options)_ Delete a role. ## BULK DELETE __knife role bulk delete REGEX__ _(options)_ Delete roles on the Chef Server based on a regular expression. The regular expression (_REGEX_) should be in quotes, not in //'s. ## DESCRIPTION Roles provide a mechanism to group repeated configuration settings. Roles are data structures that contain __default\_attributes__, and __override_attributes__, which are nested hashes of configuration settings, and a __run_list__, which is an ordered list of recipes and roles that should be applied to a host by chef-client. __default_attributes__ will be overridden if they conflict with a value on a node that includes the role. Conversely, __override_attributes__ will override any values set on nodes that apply them. When __chef-client__(8) configures a host, it will "expand" the __run_list__ included in that host's node data. The expansion process will recursively replace any roles in the run\_list with that role's run\_list. ## SEE ALSO __knife-node(1)__ __knife-environment(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-cookbook-site.mkd0000644000004100000410000001022512520074675024353 0ustar www-datawww-dataknife-cookbook-site(1) -- Install and update open source cookbooks ======================================== ## SYNOPSIS __knife__ __cookbook site__ _sub-command_ _(options)_ ## COOKBOOK SITE SUB-COMMANDS `knife cookbook site` provides the following subcommands: ## INSTALL __cookbook site install COOKBOOK [VERSION]__ _(options)_ * `-D`, `--skip-dependencies `: Skip automatic installation of dependencies. * `-o`, `--cookbook-path PATH`: Install cookbooks to PATH * `-B`, `--branch BRANCH`: Default branch to work with [defaults to master] Uses git(1) version control in conjunction with the cookbook site to install community contributed cookbooks to your local cookbook repository. Running `knife cookbook site install` does the following: 1. A new "pristine copy" branch is created in git for tracking the upstream; 2. All existing cookbooks are removed from the branch; 3. The cookbook is downloaded from the cookbook site in tarball form; 4. The downloaded cookbook is untarred, and its contents committed via git; 5. The pristine copy branch is merged into the master branch. By installing cookbook with this process, you can locally modify the upstream cookbook in your master branch and let git maintain your changes as a separate patch. When an updated upstream version becomes available, you will be able to merge the upstream changes while maintaining your local modifications. Unless _--skip-dependencies_ is specified, the process is applied recursively to all the cookbooks _COOKBOOK_ depends on (via metadata _dependencies_). ## DOWNLOAD __knife cookbook site download COOKBOOK [VERSION]__ _(options)_ * `-f`, `--file FILE`: The filename to write to * `--force`: Force download deprecated cookbook Downloads a specific cookbook from the Community site, optionally specifying a certain version. ## LIST __knife cookbook site list__ _(options)_ * `-w`, `--with-uri`: Show corresponding URIs Lists available cookbooks from the Community site. ## SEARCH __knife cookbook site search QUERY__ _(options)_ Searches for available cookbooks matching the specified query. ## SHARE __knife cookbook site share COOKBOOK CATEGORY__ _(options)_ * `-k`, `--key KEY`: API Client Key * `-u`, `--user USER`: API Client Username * `-o`, `--cookbook-path PATH:PATH`: A colon-separated path to look for cookbooks in Uploads the specified cookbook using the given category to the Opscode cookbooks site. Requires a login user and certificate for the Opscode Cookbooks site. By default, knife will use the username and API key you've configured in your configuration file; otherwise you must explicitly set these values on the command line or use an alternate configuration file. ## UNSHARE __knife cookbook site unshare COOKBOOK__ Stops sharing the specified cookbook on the Opscode cookbooks site. ## SHOW __knife cookbook site show COOKBOOK [VERSION]__ _(options)_ Shows information from the site about a particular cookbook. ## DESCRIPTION The cookbook site, , 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. `knife cookbook site` commands provide an interface to the cookbook site's HTTP API. For commands that read data from the API, no account is required. In order to upload cookbooks using the `knife cookbook site share` command, you must create an account on the cookbook site and configure your credentials via command line option or in your knife configuration file. ## EXAMPLES Uploading cookbooks to the Opscode cookbooks site: knife cookbook site share example Other -k ~/.chef/USERNAME.pem -u USERNAME ## SEE ALSO __knife-cookbook(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-environment.mkd0000644000004100000410000001253112520074675024151 0ustar www-datawww-dataknife-environment(1) -- Define cookbook policies for the environments in your infrastructure ======================================== ## SYNOPSIS __knife__ __environment__ _sub-command_ _(options)_ ## SUBCOMMANDS Environment subcommands follow a basic create, read, update, delete (CRUD) pattern. The following subcommands are available: ## CREATE __knife environment create__ _environment_ _(options)_ * `-d`, `--description DESCRIPTION`: The value of the description field. Create a new environment object on the Chef Server. The envrionment will be opened in the text editor for editing prior to creation if the -n option is not present. ## DELETE __knife environment delete__ _environment_ _(options)_ Destroy an environment on the Chef Server. A prompt for confirmation will be displayed if the -y options is not given. ## EDIT __knife environment edit__ _environment_ _(options)_ Fetch _environment_ and display it in the text editor for editing. The environment will be saved to the Chef Server when the editing session exits. ## FROM FILE __knife environment from file__ _file_ _(options)_ Create or update an environment from the JSON or Ruby format _file_. See __format__ for the proper format of this file. ## LIST __knife environment list__ _(options)_ * `-w`, `--with-uri`: Show the resource URI for each environment ## SHOW __knife environment show__ _environment_ _(options)_ ## DESCRIPTION Environments provide a means to apply policies to hosts in your infrastructure based on business function. For example, you may have a separate copy of your infrastructure called "dev" that runs the latest version of your application and should use the newest versions of your cookbooks when configuring systems, and a production instance of your infrastructure where you wish to update code and cookbooks in a more controlled fashion. In Chef, this function is implemented with _environments_. Environments contain two major components: a set of cookbook version constraints and environment attributes. ## SYNTAX A cookbook version constraint is comprised of a _cookbook name_ and a _version constraint_. The _cookbook name_ is the name of a cookbook in your system, and the _version constraint_ is a String describing the version(s) of that cookbook allowed in the environment. Only one _version constraint_ is supported for a given _cookbook name_. The exact syntax used to define a cookbook version constraint varies depending on whether you use the JSON format or the Ruby format. In the JSON format, the cookbook version constraints for an environment are represented as a single JSON object, like this: {"apache2": ">= 1.5.0"} In the Ruby format, the cookbook version constraints for an environment are represented as a Ruby Hash, like this: {"apache2" => ">= 1.5.0"} A _version number_ is a String comprised of two or three digits separated by a dot (.) character, or in other words, strings of the form "major.minor" or "major.minor.patch". "1.2" and "1.2.3" are examples of valid version numbers. Version numbers containing more than three digits or alphabetic characters are not supported. A _version constraint_ String is composed of an _operator_ and a _version number_. The following operators are available: * `= VERSION`: Equality. Only the exact version specified may be used. * `> VERSION`: Greater than. Only versions greater than `VERSION` may be used. * `>= VERSION`: Greater than or equal to. Only versions equal to VERSION or greater may be used. * `< VERSION`: Less than. Only versions less than VERSION may be used. * `<= VERSION`: Less than or equal to. Only versions lesser or equal to VERSION may be used. * `~> VERSION`: Pessimistic greater than. Depending on the number of components in the given VERSION, the constraint will be optimistic about future minor or patch revisions only. For example, `~> 1.1` will match any version less than `2.0` and greater than or equal to `1.1.0`, whereas `~> 2.0.5` will match any version less than `2.1.0` and greater than or equal to `2.0.5`. ## FORMAT The JSON format of an envioronment is as follows: { "name": "dev", "description": "The development environment", "cookbook_versions": { "couchdb": "= 11.0.0" }, "json_class": "Chef::Environment", "chef_type": "environment", "default_attributes": { "apache2": { "listen_ports": [ "80", "443" ] } }, "override_attributes": { "aws_s3_bucket": "production" } } The Ruby format of an environment is as follows: name "dev" description "The development environment" cookbook_versions "couchdb" => "= 11.0.0" default_attributes "apache2" => { "listen_ports" => [ "80", "443" ] } override_attributes "aws_s3_bucket" => "production" ## SEE ALSO __knife-node(1)__ __knife-cookbook(1)__ __knife-role(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Daniel DeLeo . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife.mkd0000644000004100000410000001745012520074675021614 0ustar www-datawww-dataknife(1) -- Chef Server API client utility ======================================== ## SYNOPSIS __knife__ _sub-command_ [_argument_...] _(options)_ ## DESCRIPTION Knife is a command-line utility used to manage data on a Chef server through the HTTP(S) API. Knife is organized into groups of subcommands centered around the various object types in Chef. Each category of subcommand is documented in its own manual page. Available topics are: * bootstrap * client * configure * cookbook-site * cookbook * data-bag * environment * exec * index * node * recipe * role * search * ssh * status * tag If the knife manuals are in your `MANPATH`, you can access help for the above topics using `man knife-TOPIC`; otherwise, you can view the documentation using `knife help TOPIC`. ## OPTIONS * `-s`, `--server-url` URL: Chef Server URL, corresponds to `Chef::Config` `chef_server_url`. * `-k`, `--key` KEY: API Client Key, corresponds to `Chef::Config` `client_key`. * `-c`, `--config` CONFIG: The configuration file to use * `-E`, `--environment ENVIRONMENT`: Set the Chef environment (except for in searches, where this will be flagrantly ignored) * `-e`, `--editor` EDITOR: Set the editor to use for interactive commands * `-F`, `--format` FORMAT: Which format to use for output. See FORMATS for details. * `-d`, `--disable-editing`: Do not open EDITOR, just accept the data as is * `-u`, `--user` USER: API Client Username, corresponds to `Chef::Config` `node_name`. * `-p`, `--print-after`: Show the data after a destructive operation * `-v`, `--version`: Show chef version * `-V`, `--verbose`: More verbose output. Use twice for max verbosity. * `-y`, `--yes`: Say yes to all prompts for confirmation * `--defaults`: Accept default values for all questions * `--[no-]color`: Use colored output. Color enabled by default. * `-h`, `--help`: Show the available options for a command. ## SUB-COMMANDS Sub-commands that operate on the basic Chef data types are structured as _NOUN verb NOUN (options)_. For all data types, the following commands are available: * create (create) * list and show (read) * edit (update) * delete (destroy) Knife also includes commands that take actions other than displaying or modifying data on the Chef Server, such as __knife-ssh(1)__. ## CONFIGURATION The knife configuration file is a Ruby DSL to set configuration parameters for Knife's __GENERAL OPTIONS__. The default location for the config file is `~/.chef/knife.rb`. If managing multiple Chef repositories, per-repository config files can be created. The file must be `.chef/knife.rb` in the current directory of the repository. If the config file exists, knife uses these settings for __GENERAL OPTIONS__ defaults. * `node_name`: User or client identity (i.e., _name_) to use for authenticating requests to the Chef Server. * `client_key`: Private key file to authenticate to the Chef server. Corresponds to the `-k` or `--key` option. * `chef_server_url`: URL of the Chef server. Corresponds to the `-s` or `--server-url` option. This is requested from the user when running this sub-command. * `syntax_check_cache_path`: Specifies the path to a directory where knife caches information about files that it has syntax checked. * `validation_client_name`: Specifies the name of the client used to validate new clients. * `validation_key`: Specifies the private key file to use when bootstrapping new hosts. See knife-client(1) for more information about the validation client. * `cookbook_copyright`, `cookbook_email`, `cookbook_license`, `readme_format` Used by `knife cookbook create` sub-command to specify the copyright holder, maintainer email, license and readme format (respectively) for new cookbooks. The copyright holder is listed as the maintainer in the cookbook's metadata and as the Copyright in the comments of the default recipe. The maintainer email is used in the cookbook metadata. The license determines what preamble to put in the comment of the default recipe, and is listed as the license in the cookbook metadata. Currently supported licenses are "apachev2" and "none". Any other values will result in an empty license in the metadata (needs to be filled in by the author), and no comment preamble in the default recipe. Currently supported readme formats are "md", "mkd", "txt", and "rdoc". Any other value will result in an unformatted README. ## FILES _~/.chef/knife.rb_ Ruby DSL configuration file for knife. See __CONFIGURATION__. ## FORMATS The amount of content displayed and the output format are modified by the `--format` option. If no alternate format is selected, the default is summary. Valid formats are: * `summary`: displays the node in a custom, summarized format (default) * `text`: displays the node data in its entirety using the colorized tree display * `json`: displays the node in JSON format * `yaml`: displays the node in YAML format * `pp`: displays the node using Ruby's pretty printer. For brevity, only the first character of the format is required, for example, -Fj will produce JSON format output. ## CHEF WORKFLOW When working with Chef and Knife in the local repository, the recommended workflow outline looks like: * Create repository. A skeleton sample is provided at _http://github.com/opscode/chef-repo/_. * Configure knife, see __CONFIGURATION__. * Download cookbooks from the Opscode cookbooks site, see __COOKBOOK SITE SUB-COMMANDS__. * Or, create new cookbooks, see `cookbook create` sub-command. * Commit changes to the version control system. See your tool's documentation. * Upload cookbooks to the Chef Server, see __COOKBOOK SUB-COMMANDS__. * Launch instances in the Cloud, OR provision new hosts; see __CLOUD COMPUTING SUB-COMMANDS__ and __BOOTSTRAP SUB-COMMANDS__. * Watch Chef configure systems! A note about git: Opscode and many folks in the Chef community use git, but it is not required, except in the case of the `cookbook site vendor` sub-command, as it uses git directly. Version control is strongly recommended though, and git fits with a lot of the workflow paradigms. ## EXAMPLES ## ENVIRONMENT * `EDITOR`: The text editor to use for editing data. The --editor option takes precedence over this value, and the --disable-editing option suppresses data editing entirely. ## SEE ALSO __chef-client(8)__ __chef-server(8)__ __chef-shell(1)__ __knife-bootstrap(1)__ __knife-client(1)__ __knife-configure(1)__ __knife-cookbook-site(1)__ __knife-cookbook(1)__ __knife-data-bag(1)__ __knife-environment(1)__ __knife-exec(1)__ __knife-index(1)__ __knife-node(1)__ __knife-recipe(1)__ __knife-role(1)__ __knife-search(1)__ __knife-ssh(1)__ __knife-tag(1)__ Complete Chef documentation is available online: JSON is JavaScript Object Notation SOLR is an open source search engine. __git(1)__ is a version control system This manual page was generated from Markdown with __ronn(1)__ ## AUTHOR Chef was written by Adam Jacob of Opscode (), with contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . ## LICENSE Both Chef and this documentation are released under the terms of the Apache 2.0 License. You may view the license online: On some systems, the complete text of the Apache 2.0 License may be found in `/usr/share/common-licenses/Apache-2.0`. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-search.mkd0000644000004100000410000001237712520074675023062 0ustar www-datawww-dataknife-search(1) -- Find objects on a Chef Server by query ======================================== ## SYNOPSIS __knife__ __search INDEX QUERY__ _(options)_ * `-a`, `--attribute ATTR`: Show only one attribute * `-i`, `--id-only`: Show only the ID of matching objects * `-q`, `--query QUERY`: The search query; useful to protect queries starting with - * `-R`, `--rows INT`: The number of rows to return * `-r`, `--run-list`: Show only the run list * `-o`, `--sort SORT`: The order to sort the results in * `-b`, `--start ROW`: The row to start returning results at * `-m`, `--medium`: Display medium sized output when searching nodes using the default summary format * `-l`, `--long`: Display long output when searching nodes using the default summary format ## DESCRIPTION Search is a feature of the Chef Server that allows you to use a full-text search engine to query information about your infrastructure and applications. You can utilize this service via search calls in a recipe or the knife search command. The search syntax is based on Lucene. ## INDEXES Search indexes are a feature of the Chef Server and the search sub-command allows querying any of the available indexes using SOLR query syntax. The following data types are indexed for search: * _node_ * _role_ * _environment_ * _clients_ * _data bag_ Data bags are indexed by the data bag's name. For example, to search a data bag named "admins": knife search admins "field:search_pattern" ## QUERY SYNTAX Queries have the form `field:search_pattern` where `field` is a key in the JSON description of the relevant objects (nodes, roles, environments, or data bags). Both `field` and `search_pattern` are case-sensitive. `search_pattern` can be an exact, wildcard, range, or fuzzy match (see below). The `field` supports exact matching and limited wildcard matching. Searches will return the relevant objects (nodes, roles, environments, or data bags) where the `search_pattern` matches the object's value of `field`. ### FIELD NAMES Field names are the keys within the JSON description of the object being searched. Nested Keys can be searched by placing an underscore ("_") between key names. ### WILDCARD MATCHING FOR FIELD NAMES The field name also has limited support for wildcard matching. Both the "*" and "?" wildcards (see below) can be used within a field name; however, they cannot be the first character of the field name. ### EXACT MATCHES Without any search modifiers, a search returns those fields for which the `search_pattern` exactly matches the value of `field` in the JSON description of the object. ### WILDCARD MATCHES Search support both single- and multi-character wildcard searches within a search pattern. '?' matches exactly one character. '*' matches zero or more characters. ### RANGE MATCHES Range searches allows one to match values between two given values. To match values between X and Y, inclusively, use square brackets: knife search INDEX 'field:[X TO Y] To match values between X and Y, exclusively, use curly brackets: knife search INDEX 'field:{X TO Y}' Values are sorted in lexicographic order. ### FUZZY MATCHES Fuzzy searches allows one to match values based on the Levenshtein Distance algorithm. To perform a fuzzy match, append a tilda (~) to the search term: knife search INDEX 'field:term~' This search would return nodes whose `field` was 'perm' or 'germ'. ### BOOLEAN OPERATORS The boolean operators NOT, AND, and OR are supported. To find values of `field` that are not X: knife search INDEX 'field:(NOT X)' To find records where `field1` is X and `field2` is Y: knife search INDEX 'field1:X AND field2:Y' To find records where `field` is X or Y: knife search INDEX 'field:X OR field:Y' ### QUOTING AND SPECIAL CHARACTERS In order to avoid having special characters and escape sequences within your search term interpreted by either Ruby or the shell, enclose them in single quotes. Search terms that include spaces should be enclosed in double-quotes: knife search INDEX 'field:"term with spaces"' The following characters must be escaped: + - && || ! ( ) { } [ ] ^ " ~ * ? : \ ## EXAMPLES Find the nodes with the fully-qualified domain name (FQDN) www.example.com: knife search node 'fqdn:www.example.com' Find the nodes running a version of Ubuntu: knife search node 'platform:ubuntu*' Find all nodes running CentOS in the production environment: knife search node 'chef_environment:production AND platform:centos' ## KNOWN BUGS * Searches against the client index return no results in most cases. (CHEF-2477) * Searches using the fuzzy match operator (~) produce an error. (CHEF-2478) ## SEE ALSO __knife-ssh__(1) [Lucene Query Parser Syntax](http://lucene.apache.org/java/2_3_2/queryparsersyntax.html) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-configure.mkd0000644000004100000410000000533112520074675023566 0ustar www-datawww-dataknife-configure(1) -- Generate configuration files for knife or Chef Client ======================================== ## SYNOPSIS __knife__ __configure__ [client] _(options)_ ## DESCRIPTION Generates a knife.rb configuration file interactively. When given the --initial option, also creates a new administrative user. ## CONFIGURE SUBCOMMANDS ## __knife configure__ _(options)_ * `-i`, `--initial`: Create an initial API Client * `-r`, `--repository REPO`: The path to your chef-repo Create a configuration file for knife. This will prompt for values to enter into the file. Default values are listed in square brackets if no other entry is typed. See __knife__(1) for a description of configuration options. __knife configure client__ _directory_ Read the _knife.rb_ config file and generate a config file suitable for use in _/etc/chef/client.rb_ and copy the validation certificate into the specified _directory_. ## EXAMPLES * On a freshly installed Chef Server, use _knife configure -i_ to create an administrator and knife configuration file. Leave the field blank to accept the default value. On most systems, the default values are acceptable. user@host$ knife configure -i Please enter the chef server URL: [http://localhost:4000] Please enter a clientname for the new client: [username] Please enter the existing admin clientname: [chef-webui] Please enter the location of the existing admin client's private key: [/etc/chef/webui.pem] Please enter the validation clientname: [chef-validator] Please enter the location of the validation key: [/etc/chef/validation.pem] Please enter the path to a chef repository (or leave blank): Creating initial API user... Created (or updated) client[username] Configuration file written to /home/username/.chef/knife.rb This creates a new administrator client named _username_, writes a configuration file to _/home/username/.chef/knife.rb_, and the private key to _/home/username/.chef/username.pem_. The configuration file and private key may be copied to another system to facilitate administration of the Chef Server from a remote system. Depending on the value given for the Chef Server URL, you may need to modify that setting after copying to a remote host. ## SEE ALSO __knife__(1) __knife-client__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-client.mkd0000644000004100000410000000634712520074675023073 0ustar www-datawww-dataknife-client(1) -- Manage Chef API Clients ======================================== ## SYNOPSIS __knife__ __client__ _sub-command_ _(options)_ ## SUB-COMMANDS Client subcommands follow a basic create, read, update, delete (CRUD) pattern. The Following subcommands are available: ## BULK DELETE __knife client bulk delete__ _regex_ _(options)_ Delete clients where the client name matches the regular expression _regex_ on the Chef Server. The regular expression should be given as a quoted string, and not surrounded by forward slashes. ## CREATE __knife client create__ _client name_ _(options)_ * `-a`, `--admin `: Create the client as an admin * `-f`, `--file FILE`: Write the key to a file Create a new client. This generates an RSA keypair. The private key will be displayed on _STDOUT_ or written to the named file. The public half will be stored on the Server. For _chef-client_ systems, the private key should be copied to the system as `/etc/chef/client.pem`. Admin clients should be created for users that will use _knife_ to access the API as an administrator. The private key will generally be copied to `~/.chef/client\_name.pem` and referenced in the `knife.rb` configuration file. ## DELETE __knife client delete__ _client name_ _(options)_ Deletes a registered client. ## EDIT __client edit__ _client name_ _(options)_ Edit a registered client. ## LIST __client list__ _(options)_ * `-w`, `--with-uri`: Show corresponding URIs List all registered clients. ## REREGISTER __client reregister__ _client name_ _(options)_ * `-f`, `--file FILE`: Write the key to a file Regenerate the RSA keypair for a client. The public half will be stored on the server and the private key displayed on _STDOUT_ or written to the named file. This operation will invalidate the previous keypair used by the client, preventing it from authenticating with the Chef Server. Use care when reregistering the validator client. ## SHOW __client show__ _client name_ _(options)_ * `-a`, `--attribute ATTR`: Show only one attribute Show a client. Output format is determined by the --format option. ## DESCRIPTION Clients are identities used for communication with the Chef Server API, roughly equivalent to user accounts on the Chef Server, except that clients only communicate with the Chef Server API and are authenticated via request signatures. In the typical case, there will be one client object on the server for each node, and the corresponding client and node will have identical names. In the Chef authorization model, there is one special client, the "validator", which is authorized to create new non-administrative clients but has minimal privileges otherwise. This identity is used as a sort of "guest account" to create a client identity when initially setting up a host for management with Chef. ## SEE ALSO __knife-node__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-cookbook.mkd0000644000004100000410000002313512520074675023415 0ustar www-datawww-dataknife-cookbook(1) -- upload and manage chef cookbooks ======================================== ## SYNOPSIS __knife__ __cookbook__ _sub-command_ _(options)_ ## SUB-COMMANDS `knife cookbook` supports the following sub commands: ## LIST __knife cookbook list__ _(options)_ * `-a`, `--all`: show all versions of a cookbook instead of just the most recent * `-w`, `--with-uri`: show corresponding uris Lists the cookbooks available on the Chef server. ## SHOW __knife cookbook show cookbook [version] [part] [filename]__ _(options)_ * `-f`, `--fqdn fqdn `: the fqdn of the host to see the file for * `-p`, `--platform platform `: the platform to see the file for * `-v`, `--platform-version version`: the platform version to see the file for * `-w`, `--with-uri`: Show corresponding URIs show a particular part of a _cookbook_ for the specified _version_. _part_ can be one of: * _attributes_ * _definitions_ * _files_ * _libraries_ * _providers_ * _recipes_ * _resources_ * _templates_ ## UPLOAD __knife cookbook upload [cookbooks...]__ _(options)_ * `-a`, `--all`: upload all cookbooks, rather than just a single cookbook * `-o`, `--cookbook-path path:path`: a colon-separated path to look for cookbooks in * `-d`, `--upload-dependencies`: Uploads additional cookbooks that this cookbook lists in as dependencies in its metadata. * `-E`, `--environment ENVIRONMENT`: An _ENVIRONMENT_ to apply the uploaded cookbooks to. Specifying this option will cause knife to edit the _ENVIRONMENT_ to place a strict version constraint on the cookbook version(s) uploaded. * `--freeze`: Sets the frozen flag on the uploaded cookbook(s) Any future attempt to modify the cookbook without changing the version number will return an error unless --force is specified. * `--force`: Overrides the frozen flag on a cookbook, allowing you to overwrite a cookbook version that has previously been uploaded with the --freeze option. Uploads one or more cookbooks from your local cookbook repository(ies) to the Chef Server. Only files that don't yet exist on the server will be uploaded. As the command parses the name args as 1..n cookbook names: `knife cookbook upload COOKBOOK COOKBOOK ...` works for one to many cookbooks. ## DOWNLOAD __knife cookbook download cookbook [version]__ _(options)_ * `-d`, `--dir download_directory`: the directory to download the cookbook into * `-f`, `--force`: overwrite an existing directory with the download * `-n`, `--latest`: download the latest version of the cookbook download a cookbook from the chef server. if no version is specified and only one version exists on the server, that version will be downloaded. if no version is specified and multiple versions are available on the server, you will be prompted for a version to download. ## DELETE __knife cookbook delete cookbook [version]__ _(options)_ * `-a`, `--all`: delete all versions * `-p`, `--purge`: purge files from backing store. this will disable any cookbook that contains any of the same files as the cookbook being purged. delete the specified _version_ of the named _cookbook_. if no version is specified, and only one version exists on the server, that version will be deleted. if multiple versions are available on the server, you will be prompted for a version to delete. ## BULK DELETE __knife cookbook bulk delete regex__ _(options)_ * `-p`, `--purge`: purge files from backing store. this will disable any cookbook that contains any of the same files as the cookbook being purged. delete cookbooks on the chef server based on a regular expression. the regular expression (_regex_) should be in quotes, not in //'s. ## COOKBOOK CREATE __knife cookbook create cookbook__ _(options)_ * `-o`, `--cookbook-path path`: the directory where the cookbook will be created * `-r`, `--readme-format format`: format of the readme file md, mkd, txt, rdoc * `-C`, `--copyright copyright`: name of copyright holder * `-i`, `--license license`: license for cookbook, apachev2 or none * `-m`, `--email email`: email address of cookbook maintainer this is a helper command that creates a new cookbook directory in the `cookbook_path`. the following directories and files are created for the named cookbook. * cookbook/attributes * cookbook/definitions * cookbook/files/default * cookbook/libraries * cookbook/metadata.rb * cookbook/providers * cookbook/readme.md * cookbook/recipes/default.rb * cookbook/resources * cookbook/templates/default supported readme formats are 'md' (default), 'mkd', 'txt', 'rdoc'. the readme file will be written with the specified extension and a set of helpful starting headers. specify `-C` or `--copyright` with the name of the copyright holder as your name or your company/organization name in a quoted string. if this value is not specified an all-caps string `your_company_name` is used which can be easily changed with find/replace. specify `-i` or `--license` with the license that the cookbook is distributed under for sharing with other people or posting to the opscode cookbooks site. be aware of the licenses of files you put inside the cookbook and follow any restrictions they describe. when using `none` (default) or `apachev2`, comment header text and metadata file are pre-filled. the `none` license will be treated as non-redistributable. specify `-m` or `--email` with the email address of the cookbook's maintainer. if this value is not specified, an all-caps string `your_email` is used which can easily be changed with find/replace. the cookbook copyright, license, email and readme_format settings can be filled in the `knife.rb`, for example with default values: cookbook_copyright "your_company_name" cookbook_license "none" cookbook_email "your_email" readme_format "md" ## METADATA __knife cookbook metadata cookbook__ _(options)_ * `-a`, `--all`: generate metadata for all cookbooks, rather than just a single cookbook * `-o`, `--cookbook-path path:path`: a colon-separated path to look for cookbooks in generate cookbook metadata for the named _cookbook_. the _path_ used here specifies where the cookbooks directory is located and corresponds to the `cookbook_path` configuration option. ## METADATA FROM FILE __knife cookbook metadata from file__ _(options)_ load the cookbook metadata from a specified file. ## TEST __knife cookbook test [cookbooks...]__ _(options)_ * `-a`, `--all`: test all cookbooks, rather than just a single cookbook * `-o`, `--cookbook-path path:path`: a colon-separated path to look for cookbooks in test the specified cookbooks for syntax errors. this uses the built-in ruby syntax checking option for files in the cookbook ending in `.rb`, and the erb syntax check for files ending in `.erb` (templates). ## RECIPE LIST __knife recipe list [PATTERN]__ List available recipes from the server. Specify _PATTERN_ as a regular expression to limit the results. ## DESCRIPTION Cookbooks are the fundamental unit of distribution in Chef. They encapsulate all recipes of resources and assets used to configure a particular aspect of the infrastructure. The following sub-commands can be used to manipulate the cookbooks stored on the Chef Server. On disk, cookbooks are directories with a defined structure. The following directories may appear within a cookbook: * COOKBOOK/attributes/: Ruby files that define default parameters to be used in recipes * COOKBOOK/definitions/: Ruby files that contain _resource definitions_ * COOKBOOK/files/SPECIFICITY: Files of arbitrary type. These files may be downloaded by chef-client(8) when configuring a host. * COOKBOOK/libraries/: Ruby files that contain library code needed for recipes * COOKBOOK/providers/: Ruby files that contain Lightweight Provider definitions * COOKBOOK/recipes/: Ruby files that use Chef's recipe DSL to describe the desired configuration of a system * COOKBOOK/resources/: Ruby files that contain Lightweight Resource definitions * COOKBOOK/templates/SPECIFICITY: ERuby (ERb) template files. These are referenced by _recipes_ and evaluated to dynamically generate configuration files. __SPECIFICITY__ is a feature of _files_ and _templates_ that allow you to specify alternate files to be used on a specific OS platform or host. The default specificity setting is _default_, that is files in `COOKBOOK/files/default` will be used when a more specific copy is not available. Further documentation for this feature is available on the Chef wiki: Cookbooks also contain a metadata file that defines various properties of the cookbook. The most important of these are the _version_ and the _dependencies_. The _version_ is used in combination with environments to select which copy of a given cookbook is distributed to a node. The _dependencies_ are used by the server to determine which additional cookbooks must be distributed to a given host when it requires a cookbook. ## SEE ALSO __knife-environment(1)__ __knife-cookbook-site(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/chef-shell.mkd0000644000004100000410000001471712520074675022535 0ustar www-datawww-datachef-shell(1) -- Interactive Chef Console ========================================= ## SYNOPSIS __chef-shell__ [_named configuration_] _(options)_ * `-S`, `--server CHEF_SERVER_URL`: The chef server URL * `-z`, `--client`: chef-client mode * `-c`, `--config CONFIG`: The configuration file to use * `-j`, `--json-attributes JSON_ATTRIBS`: Load attributes from a JSON file or URL * `-l`, `--log-level LOG_LEVEL`: Set the logging level * `-s`, `--solo`: chef-solo session * `-a`, `--standalone`: standalone session * `-v`, `--version`: Show chef version * `-h`, `--help`: Show command options When no --config option is specified, chef-shell attempts to load a default configuration file: * If a _named configuration_ is given, chef-shell will load ~/.chef/_named configuration_/chef_shell.rb * If no _named configuration_ is given chef-shell will load ~/.chef/chef_shell.rb if it exists * chef-shell falls back to loading /etc/chef/client.rb or /etc/chef/solo.rb if -z or -s options are given and no chef_shell.rb can be found. * The --config option takes precedence over implicit configuration paths. ## DESCRIPTION `chef-shell` is an irb(1) (interactive ruby) session customized for Chef. `chef-shell` serves two primary functions: it provides a means to interact with a Chef Server interactively using a convenient DSL; it allows you to define and run Chef recipes interactively. ## SYNTAX chef-shell uses irb's subsession feature to provide multiple modes of interaction. In addition to the primary mode which is entered on start, `recipe` and `attributes` modes are available. ## PRIMARY MODE The following commands are available in the primary session: * `help`: Prints a list of available commands * `version`: Prints the Chef version * `recipe`: Switches to `recipe` mode * `attributes`: Switches to `attributes` mode * `run_chef`: Initiates a chef run * `reset`: reinitializes chef-shell session * `echo :on|:off`: Turns irb's echo function on or off. Echo is _on_ by default. * `tracing :on|:off`: Turns irb's function tracing feature on or off. Tracing is extremely verbose and expected to be of interest primarily to developers. * `node`: Returns the _node_ object for the current host. See knife-node(1) for more information about nodes. * `ohai`: Prints the attributes of _node_ In addition to these commands, chef-shell provides a DSL for accessing data on the Chef Server. When working with remote data in chef-shell, you chain method calls in the form _object type_._operation_, where _object type_ is in plural form. The following object types are available: * `nodes` * `roles` * `data_bags` * `clients` * `cookbooks` For each _object type_ the following operations are available: * _object type_.all(_&block_): Loads all items from the server. If the optional code _block_ is given, each item will be passed to the block and the results returned, similar to ruby's `Enumerable#map` method. * _object type_.show(_object name_): Aliased as _object type_.load Loads the singular item identified by _object name_. * _object type_.search(_query_, _&block_): Aliased as _object type_.find Runs a search against the server and returns the matching items. If the optional code _block_ is given each item will be passed to the block and the results returned. The _query_ may be a Solr/Lucene format query given as a String, or a Hash of conditions. If a Hash is given, the options will be ANDed together. To join conditions with OR, use negative queries, or any advanced search syntax, you must provide give the query in String form. * _object type_.transform(:all|_query_, _&block_): Aliased as _object type_.bulk_edit Bulk edit objects by processing them with the (required) code _block_. You can edit all objects of the given type by passing the Symbol `:all` as the argument, or only a subset by passing a _query_ as the argument. The _query_ is evaluated in the same way as with __search__. The return value of the code _block_ is used to alter the behavior of `transform`. If the value returned from the block is `nil` or `false`, the object will not be saved. Otherwise, the object is saved after being passed to the block. This behavior can be exploited to create a dry run to test a data transformation. ## RECIPE MODE Recipe mode implements Chef's recipe DSL. Exhaustively documenting this DSL is outside the scope of this document. See the following pages in the Chef documentation for more information: * * Once you have defined resources in the recipe, you can trigger a convergence run via `run_chef` ## EXAMPLES * A "Hello World" interactive recipe chef > recipe chef:recipe > echo :off chef:recipe > file "/tmp/hello\_world" chef:recipe > run\_chef [Sat, 09 Apr 2011 08:56:56 -0700] INFO: Processing file[/tmp/hello\_world] action create ((irb#1) line 2) [Sat, 09 Apr 2011 08:56:56 -0700] INFO: file[/tmp/hello\_world] created file /tmp/hello\_world chef:recipe > pp ls '/tmp' [".", "..", "hello\_world"] * Search for _nodes_ by role, and print their IP addresses chef > nodes.find(:roles => 'monitoring-server') {|n| n[:ipaddress] } => ["10.254.199.5"] * Remove the role _obsolete_ from every node in the system chef > nodes.transform(:all) {|n| n.run\_list.delete('role[obsolete]') } => [node[chef098b2.opschef.com], node[ree-woot], node[graphite-dev], node[fluke.localdomain], node[ghost.local], node[kallistec]] ## BUGS `chef-shell` often does not perfectly replicate the context in which chef-client(8) configures a host, which may lead to discrepancies in observed behavior. `chef-shell` has to duplicate much code from chef-client's internal libraries and may become out of sync with the behavior of those libraries. ## SEE ALSO chef-client(8) knife(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. chef-shell was written by Daniel DeLeo. ## DOCUMENTATION This manual page was written by Daniel DeLeo . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF chef-shell is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-bootstrap.mkd0000644000004100000410000001255412520074675023627 0ustar www-datawww-dataknife-bootstrap(1) -- Install Chef Client on a remote host ======================================== ## SYNOPSIS __knife__ __bootstrap__ _(options)_ * `-i`, `--identity-file IDENTITY_FILE`: The SSH identity file used for authentication * `-N`, `--node-name NAME`: The Chef node name for your new node * `-P`, `--ssh-password PASSWORD`: The ssh password * `-x`, `--ssh-user USERNAME`: The ssh username * `-p`, `--ssh-port PORT`: The ssh port * `--bootstrap-version VERSION`: The version of Chef to install * `--bootstrap-proxy PROXY_URL`: `The proxy server for the node being bootstrapped` * `--prerelease`: Install pre-release Chef gems * `-r`, `--run-list RUN_LIST`: Comma separated list of roles/recipes to apply * `--template-file TEMPLATE`: Full path to location of template to use * `--sudo`: Execute the bootstrap via sudo * `-d`, `--distro DISTRO`: Bootstrap a distro using a template * `--[no-]host-key-verify`: Enable host key verification, which is the default behavior. * `--hint HINT_NAME[=HINT_FILE]`: Provide the name of a hint (with option JSON file) to set for use by Ohai plugins. ## DESCRIPTION Performs a Chef Bootstrap on the target node. The goal of the bootstrap is to get Chef installed on the target system so it can run Chef Client with a Chef Server. The main assumption is a baseline OS installation exists. This sub-command is used internally by some cloud computing plugins. The bootstrap sub-command supports supplying a template to perform the bootstrap steps. If the distro is not specified (via `-d` or `--distro` option), an Ubuntu 10.04 host bootstrapped with RubyGems is assumed. The __DISTRO__ value corresponds to the base filename of the template, in other words `DISTRO`.erb. A template file can be specified with the `--template-file` option in which case the __DISTRO__ is not used. The sub-command looks in the following locations for the template to use: * `bootstrap` directory in the installed Chef Knife library. * `bootstrap` directory in the `$PWD/.chef`. * `bootstrap` directory in the users `$HOME/.chef`. The default bootstrap templates are scripts that get copied to the target node (FQDN). The following distros are supported: * centos5-gems * fedora13-gems * ubuntu10.04-gems * ubuntu10.04-apt The gems installations will use RubyGems 1.3.6 and Chef installed as a gem. The apt installation will use the Opscode APT repository. In addition to handling the software installation, these bootstrap templates do the following: - Write the validation.pem per the local knife configuration. - Write a default config file for Chef (`/etc/chef/client.rb`) using values from the `knife.rb`. - Create a JSON attributes file containing the specified run list and run Chef. In the case of the RubyGems, the `client.rb` will be written from scratch with a minimal set of values; see __EXAMPLES__. In the case of APT Package installation, `client.rb` will have the `validation_client_name` appended if it is not set to `chef-validator` (default config value), and the `node_name` will be added if `chef_node_name` option is specified. When this is complete, the bootstrapped node will have: - Latest Chef version installed from RubyGems or APT Packages from Opscode. This may be a later version than the local system. - Be validated with the configured Chef Server. - Have run Chef with its default run list if one is specified. Additional custom bootstrap templates can be created and stored in `.chef/bootstrap/DISTRO.erb`, replacing __DISTRO__ with the value passed with the `-d` or `--distro` option. See __EXAMPLES__ for more information. ## EXAMPLES Setting up a custom bootstrap is fairly straightforward. Create a `.chef/bootstrap` directory in your Chef Repository or in `$HOME/.chef/bootstrap`. Then create the ERB template file. mkdir ~/.chef/bootstrap vi ~/.chef/bootstrap/debian5.0-apt.erb For example, to create a new bootstrap template that should be used when setting up a new Debian node. Edit the template to run the commands, set up the validation certificate and the client configuration file, and finally to run chef-client on completion. The bootstrap template can be called with: knife bootstrap mynode.example.com --template-file ~/.chef/bootstrap/debian5.0-apt.erb Or, knife bootstrap mynode.example.com --distro debian5.0-apt The `--distro` parameter will automatically look in the `~/.chef/bootstrap` directory for a file named `debian5.0-apt.erb`. Templates provided by the Chef installation are located in `BASEDIR/lib/chef/knife/bootstrap/*.erb`, where _BASEDIR_ is the location where the package or Gem installed the Chef client libraries. ## BUGS `knife bootstrap` is not capable of bootstrapping multiple hosts in parallel. The bootstrap script is passed as an argument to sh(1) on the remote system, so sensitive information contained in the script will be visible to other users via the process list using tools such as ps(1). ## SEE ALSO __knife-ssh__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-index.mkd0000644000004100000410000000176512520074675022723 0ustar www-datawww-dataknife-index(1) -- Rebuild the search index on a Chef Server ======================================== ## SYNOPSIS __knife__ __index rebuild__ _(options)_ * `-y`, `--yes`: don't bother to ask if I'm sure ## DESCRIPTION Rebuilds all the search indexes on the server. This is accomplished by deleting all objects from the search index, and then forwarding each item in the database to __chef-expander__(8) via __rabbitmq-server__(1). Depending on the number of objects in the database, it may take some time for all objects to be indexed and available for search. ## SEE ALSO __knife-search__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-tag.mkd0000644000004100000410000000160312520074675022356 0ustar www-datawww-dataknife-tag(1) -- Apply tags to nodes on a Chef Server ======================================== ## SYNOPSIS __knife__ __tag__ _subcommand_ _(options)_ ## TAG SUBCOMMANDS The following `tag` subcommands are available: ## CREATE __knife tag create__ _node_ _tag_ [_..._] Adds one or more tags to _node_ ## DELETE __knife tag delete__ _node_ _tag_ [_..._] Removes one or more tags from _node_ ## LIST __knife tag list__ _node_ Lists the tags applied to _node_ ## SEE ALSO __knife-node(1)__ ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Daniel DeLeo . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. chef-12.3.0/distro/common/markdown/man1/knife-data-bag.mkd0000644000004100000410000001027412520074675023247 0ustar www-datawww-dataknife-data-bag(1) -- Store arbitrary data on a Chef Server ======================================== ## SYNOPSIS __knife__ __data bag__ _sub-command_ _(options)_ ## DESCRIPTION Data bags are stores of arbitrary JSON data. Each data bag is a collection that may contain many items. Data Bag Items are indexed by the Chef Server and can be searched via __knife-search__(1). Data bags are available to all nodes configured by __chef-client__(8), and are therefore a convenient mechanism to store global information, such as lists of administrative accounts that should be configured on all hosts. ## DATA BAG SUB-COMMANDS ## CREATE __knife data bag create__ _bag name_ [item id] _(options)_ * `-s`, `--secret SECRET`: A secret key used to encrypt the data bag item. See __encryption support__ below. * `--secret-file SECRET_FILE`: The path to a file containing the secret key to be used to encrypt the data bag item. If _item id_ is given, creates a new, empty data bag item and opens it for editing in your editor. The data bag will be created if it does not exist. If _item id_ is not given, the data bag will be created. ## DELETE __knife data bag delete__ _bag name_ [item id] _(options)_ Delete a data bag, or an item from a data bag. ## EDIT __knife data bag edit__ _bag name_ _item id_ _(options)_ * `-s`, `--secret SECRET`: A secret key used to encrypt the data bag item. See __encryption support__ below. * `--secret-file SECRET_FILE`: The path to a file containing the secret key to be used to encrypt the data bag item. Edit an item in a data bag. ## FROM FILE __knife data bag from file__ _bag name_ _file_ _(options)_ __knife data bag from file__ _bag name_ _file1_ _file2_ _file3_ _(options)_ __knife data bag from file__ _bag name_ _folder_ _(options)_ * `-s`, `--secret SECRET`: A secret key used to encrypt the data bag item. See __encryption support__ below. * `--secret-file SECRET_FILE`: The path to a file containing the secret key to be used to encrypt the data bag item. Load a data bag item from a JSON file. If _file_ is a relative or absolute path to the file, that file will be used. Otherwise, the _file_ parameter is treated as the base name of a data bag file in a Chef repository, and `knife` will search for the file in `./data_bags/bag_name/file`. For example `knife data bag from file users dan.json` would attempt to load the file `./data_bags/users/dan.json`. ## LIST __knife data bag list__ _(options)_ * `-w`, `--with-uri`: Show corresponding URIs Lists the data bags that exist on the Chef Server. ## SHOW __knife data bag show BAG [ITEM]__ _(options)_ * `-s`, `--secret SECRET`: A secret key used to encrypt the data bag item. See __encryption support__ below. * `--secret-file SECRET_FILE`: The path to a file containing the secret key to be used to encrypt the data bag item. Show a specific data bag or an item in a data bag. The output will be formatted according to the --format option. ## ENCRYPTION SUPPORT Data Bag Items may be encrypted to keep their contents secret. This may be desirable when storing sensitive information such as database passwords, API keys, etc. Data Bag Item encryption uses the AES-256 CBC symmetric key algorithm. __CAVEATS:__ Keys are not encrypted; only values are encrypted. The "id" of a Data Bag Item is not encrypted, since it is used by Chef Server to store the item in its database. For example, given the following data bag item: {"id": "important_passwords", "secret_password": "opensesame"} The key "secret\_password" will be visible to an evesdropper, but the value "opensesame" will be protected. Both the key "id" and its value "important\_passwords" will be visible to an evesdropper. Chef Server does not provide a secure mechanism for distributing encryption keys. ## SEE ALSO __knife-search__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef. http://wiki.opscode.com/display/chef/Home chef-12.3.0/distro/common/markdown/man1/knife-ssh.mkd0000644000004100000410000000471112520074675022403 0ustar www-datawww-dataknife-ssh(1) -- Run a command or interactive session on multiple remote hosts ======================================== ## SYNOPSIS __knife__ __ssh QUERY COMMAND__ _(options)_ * `-a`, `--attribute ATTR `: The attribute to use for opening the connection - default is fqdn * `-C`, `--concurrency NUM `: The number of concurrent connections * `-m`, `--manual-list `: QUERY is a space separated list of servers * `-P`, `--ssh-password PASSWORD`: The ssh password * `-x`, `--ssh-user USERNAME `: The ssh username * `-i`, `--identity-file IDENTITY_FILE`: The SSH identity file used for authentication * `-p`, `--ssh-port PORT`: The ssh port * `--[no-]host-key-verify`: Verify host key, enabled by default. ## DESCRIPTION The _ssh_ sub-command opens an ssh session to each of the nodes in the search results of the _QUERY_. This sub-command requires that the net-ssh-multi and highline Ruby libraries are installed. On Debian systems, these are the libnet-ssh-multi-ruby and libhighline-ruby packages. They can also be installed as RubyGems (net-ssh-multi and highline, respectively). ## TERMINAL MULTIPLEXING AND TERMINAL TAB SUPPORT `knife ssh` integrates with several terminal multiplexer programs to provide a more convenient means of managing multiple ssh sessions. When the _COMMAND_ option matches one of these, `knife ssh` will create multiple interactive ssh sessions running locally in the terminal multiplexer instead of invoking the command on the remote host. The available multiplexers are: * `interactive`: A built-in multiplexer. `interactive` supports running commands on a subset of the connected hosts in parallel * __screen__(1): Runs ssh interactively inside `screen`. ~/.screenrc will be sourced if it exists. * __tmux__(1): Runs ssh interactively inside tmux. * `macterm` (Mac OS X only): Opens a Terminal.app window and creates a tab for each ssh session. You must install the rb-appscript gem before you can use this option. ## SEE ALSO __knife-search__(1) ## AUTHOR Chef was written by Adam Jacob with many contributions from the community. ## DOCUMENTATION This manual page was written by Joshua Timberman . Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License. ## CHEF Knife is distributed with Chef.